diff --git a/pom.xml b/pom.xml
index c5f07e8..d31eb79 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,6 +102,7 @@ Wisconsin-Madison, and Friedrich Miescher Institute for Biomedical Research.</li
 
 		<!-- NB: Deploy releases to the SciJava Maven repository. -->
 		<releaseProfiles>deploy-to-scijava</releaseProfiles>
+		<scijava-common.version>2.84.0</scijava-common.version>
 	</properties>
 
 	<dependencies>
diff --git a/src/main/java/org/scijava/table/io/DefaultTableIOService.java b/src/main/java/org/scijava/table/io/DefaultTableIOService.java
index 6e7ee51..0049a51 100644
--- a/src/main/java/org/scijava/table/io/DefaultTableIOService.java
+++ b/src/main/java/org/scijava/table/io/DefaultTableIOService.java
@@ -31,49 +31,41 @@
 package org.scijava.table.io;
 
 import java.io.IOException;
+import java.net.URISyntaxException;
 
+import org.scijava.io.AbstractTypedIOService;
 import org.scijava.io.IOPlugin;
 import org.scijava.io.IOService;
+import org.scijava.io.location.Location;
 import org.scijava.plugin.Parameter;
 import org.scijava.plugin.Plugin;
-import org.scijava.service.AbstractService;
 import org.scijava.service.Service;
 import org.scijava.table.Table;
 
 @Plugin(type = Service.class)
-public class DefaultTableIOService extends AbstractService implements
+public class DefaultTableIOService extends AbstractTypedIOService<Table<?, ?>> implements
 	TableIOService
 {
 
-	@Parameter
-	private IOService ioService;
-
 	@Override
-	public boolean canOpen(String source) {
-		IOPlugin<?> opener = ioService.getOpener(source);
+	public boolean canOpen(Location source) {
+		IOPlugin<?> opener = ioService().getOpener(source);
 		if (opener == null) return false;
 		return Table.class.isAssignableFrom(opener.getDataType());
 	}
 
 	@Override
-	public boolean canSave(Table<?, ?> table, String destination) {
-		IOPlugin<Table<?, ?>> saver = ioService.getSaver(table, destination);
-		if (saver == null) return false;
-		return saver.supportsSave(destination);
-	}
-
-	@Override
-	public Table<?, ?> open(String source) throws IOException {
-		IOPlugin<?> opener = ioService.getOpener(source);
-		if (opener != null && Table.class.isAssignableFrom(opener.getDataType())) {
-			return (Table<?, ?>) opener.open(source);
+	public Table<?, ?> open(String source, TableIOOptions options) throws IOException {
+		try {
+			return open(locationService().resolve(source), options);
+		} catch (URISyntaxException e) {
+			throw new IOException(e);
 		}
-		throw new UnsupportedOperationException("No compatible opener found.");
 	}
 
 	@Override
-	public Table<?, ?> open(String source, TableIOOptions options) throws IOException {
-		IOPlugin<?> opener = ioService.getOpener(source);
+	public Table<?, ?> open(Location source, TableIOOptions options) throws IOException {
+		IOPlugin<?> opener = ioService().getOpener(source);
 		if (opener != null && Table.class.isAssignableFrom(opener.getDataType())
 			&& TableIOPlugin.class.isAssignableFrom(opener.getClass())) {
 			return ((TableIOPlugin)opener).open(source, options);
@@ -82,19 +74,17 @@ public boolean canSave(Table<?, ?> table, String destination) {
 	}
 
 	@Override
-	public void save(Table<?, ?> table, String destination) throws IOException {
-		IOPlugin<Table<?, ?>> saver = ioService.getSaver(table, destination);
-		if (saver != null) {
-			saver.save(table, destination);
-		}
-		else {
-			throw new UnsupportedOperationException("No compatible saver found.");
+	public void save(Table<?, ?> table, String destination, TableIOOptions options) throws IOException {
+		try {
+			save(table, locationService().resolve(destination), options);
+		} catch (URISyntaxException e) {
+			throw new IOException(e);
 		}
 	}
 
 	@Override
-	public void save(Table<?, ?> table, String destination, TableIOOptions options) throws IOException {
-		IOPlugin<Table> saver = ioService.getSaver(table, destination);
+	public void save(Table<?, ?> table, Location destination, TableIOOptions options) throws IOException {
+		IOPlugin<Table> saver = ioService().getSaver(table, destination);
 		if (saver != null && TableIOPlugin.class.isAssignableFrom(saver.getClass())) {
 			((TableIOPlugin)saver).save(table, destination, options);
 		}
@@ -102,4 +92,5 @@ public void save(Table<?, ?> table, String destination, TableIOOptions options)
 			throw new UnsupportedOperationException("No compatible saver found.");
 		}
 	}
+
 }
diff --git a/src/main/java/org/scijava/table/io/TableIOPlugin.java b/src/main/java/org/scijava/table/io/TableIOPlugin.java
index a1de241..6fa5b05 100644
--- a/src/main/java/org/scijava/table/io/TableIOPlugin.java
+++ b/src/main/java/org/scijava/table/io/TableIOPlugin.java
@@ -30,7 +30,8 @@
 
 package org.scijava.table.io;
 
-import org.scijava.io.AbstractIOPlugin;
+import org.scijava.io.IOPlugin;
+import org.scijava.io.location.Location;
 import org.scijava.table.Table;
 
 import java.io.IOException;
@@ -40,32 +41,30 @@
  *
  * @author Deborah Schmidt
  */
-public class TableIOPlugin extends AbstractIOPlugin<Table> {
+public interface TableIOPlugin extends IOPlugin<Table> {
 
 	@Override
-	public Table<?, ?> open(String source) throws IOException {
+	default Table<?, ?> open(Location source) throws IOException {
 		return open(source, new TableIOOptions());
 	}
 
 	/** Opens data from the given source. */
-	@SuppressWarnings("unused")
-	public Table<?, ?> open(final String source, final TableIOOptions options) throws IOException {
+	default Table<?, ?> open(final Location source, final TableIOOptions options) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void save(Table data, String destination) throws IOException {
+	default void save(Table data, Location destination) throws IOException {
 		save(data, destination, new TableIOOptions());
 	}
 
 	/** Saves the given data to the specified destination. */
-	@SuppressWarnings("unused")
-	public void save(final Table<?, ?> data, final String destination, final TableIOOptions options) throws IOException {
+	default void save(final Table<?, ?> data, final Location destination, final TableIOOptions options) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public Class<Table> getDataType() {
+	default Class<Table> getDataType() {
 		return Table.class;
 	}
 }
diff --git a/src/main/java/org/scijava/table/io/TableIOService.java b/src/main/java/org/scijava/table/io/TableIOService.java
index a021180..d6073aa 100644
--- a/src/main/java/org/scijava/table/io/TableIOService.java
+++ b/src/main/java/org/scijava/table/io/TableIOService.java
@@ -32,18 +32,25 @@
 
 import java.io.IOException;
 
+import org.scijava.io.IOService;
+import org.scijava.io.TypedIOService;
+import org.scijava.io.location.Location;
 import org.scijava.service.SciJavaService;
 import org.scijava.table.Table;
 
-public interface TableIOService extends SciJavaService {
+public interface TableIOService extends TypedIOService<Table<?, ?>> {
 
-	boolean canOpen(String source);
-
-	boolean canSave(Table<?, ?> table, String destination);
-
-	Table<?, ?> open(String source) throws IOException;
+	@Override
+	default Table<?, ?> open(Location source) throws IOException {
+		return open(source, TableIOOptions.options());
+	}
 	Table<?, ?> open(String source, TableIOOptions options) throws IOException;
+	Table<?, ?> open(Location source, TableIOOptions options) throws IOException;
 
-	void save(Table<?, ?> table, String destination) throws IOException;
+	@Override
+	default void save(Table<?, ?> table, Location destination) throws IOException {
+		save(table, destination, TableIOOptions.options());
+	}
 	void save(Table<?, ?> table, String destination, TableIOOptions options) throws IOException;
+	void save(Table<?, ?> table, Location destination, TableIOOptions options) throws IOException;
 }
diff --git a/src/test/java/org/scijava/table/io/TableIOServiceTest.java b/src/test/java/org/scijava/table/io/TableIOServiceTest.java
index c487609..979c1ad 100644
--- a/src/test/java/org/scijava/table/io/TableIOServiceTest.java
+++ b/src/test/java/org/scijava/table/io/TableIOServiceTest.java
@@ -34,7 +34,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.scijava.Context;
+import org.scijava.io.AbstractIOPlugin;
 import org.scijava.io.IOPlugin;
+import org.scijava.io.location.Location;
 import org.scijava.plugin.PluginInfo;
 import org.scijava.plugin.PluginService;
 import org.scijava.table.DefaultGenericTable;
@@ -107,16 +109,16 @@ public void testTableIOServiceWithOptions() {
 	}
 
 	@SuppressWarnings("rawtypes")
-	public static class FakeTableIOPlugin extends TableIOPlugin {
+	public static class FakeTableIOPlugin extends AbstractIOPlugin<Table> implements TableIOPlugin {
 
 		@Override
-		public boolean supportsOpen(String loc) {
-			return loc.endsWith("fakeTable");
+		public boolean supportsOpen(Location loc) {
+			return loc.getName().endsWith("fakeTable");
 		}
 
 		@Override
-		public boolean supportsSave(String loc) {
-			return loc.endsWith("fakeTable");
+		public boolean supportsSave(Location loc) {
+			return loc.getName().endsWith("fakeTable");
 		}
 
 		/**
@@ -124,7 +126,7 @@ public boolean supportsSave(String loc) {
 		 * It creates a row and a column with header names based on the {@param options}.
 		 */
 		@Override
-		public Table open(String loc, TableIOOptions options) {
+		public Table open(Location loc, TableIOOptions options) {
 			DefaultGenericTable table = new DefaultGenericTable();
 			if(options.values.readColumnHeaders()) {
 				table.appendColumn(String.valueOf(options.values.columnDelimiter()));