diff --git a/briar-api/src/net/sf/briar/api/serial/Reader.java b/briar-api/src/net/sf/briar/api/serial/Reader.java
index 17146a950d34cc9fa603c1a30cec89ca7ae03d5e..bdb0c3eff6c3bd8867e862c005e6075aa917a6e9 100644
--- a/briar-api/src/net/sf/briar/api/serial/Reader.java
+++ b/briar-api/src/net/sf/briar/api/serial/Reader.java
@@ -18,9 +18,6 @@ public interface Reader {
 	void addConsumer(Consumer c);
 	void removeConsumer(Consumer c);
 
-	void addStructReader(int id, StructReader<?> r);
-	void removeStructReader(int id);
-
 	boolean hasBoolean() throws IOException;
 	boolean readBoolean() throws IOException;
 
@@ -68,6 +65,5 @@ public interface Reader {
 	void readNull() throws IOException;
 
 	boolean hasStruct(int id) throws IOException;
-	<T> T readStruct(int id, Class<T> t) throws IOException;
 	void readStructId(int id) throws IOException;
 }
diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java
index 35d0e5f42e2c27c47c7962f2c16bb828e1d0ae1b..5c209944869cb49abf919fd868762e29b1f51713 100644
--- a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java
+++ b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java
@@ -1,10 +1,10 @@
 package net.sf.briar.protocol;
 
 import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
-import static net.sf.briar.api.protocol.Types.GROUP;
 import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -29,9 +29,10 @@ class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> {
 		r.addConsumer(counting);
 		r.readStructId(SUBSCRIPTION_UPDATE);
 		// Read the subscriptions
-		r.addStructReader(GROUP, groupReader);
-		List<Group> subs = r.readList(Group.class);
-		r.removeStructReader(GROUP);
+		List<Group> subs = new ArrayList<Group>();
+		r.readListStart();
+		while(!r.hasListEnd()) subs.add(groupReader.readStruct(r));
+		r.readListEnd();
 		// Read the version number
 		long version = r.readInt64();
 		if(version < 0L) throw new FormatException();
diff --git a/briar-core/src/net/sf/briar/serial/ReaderImpl.java b/briar-core/src/net/sf/briar/serial/ReaderImpl.java
index 56fab7d60b6beedca808bd286516f188d45d7d69..47f5849ba0412002f902537a85f08d71963639f1 100644
--- a/briar-core/src/net/sf/briar/serial/ReaderImpl.java
+++ b/briar-core/src/net/sf/briar/serial/ReaderImpl.java
@@ -12,7 +12,6 @@ import java.util.Map;
 import net.sf.briar.api.Bytes;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.serial.Consumer;
-import net.sf.briar.api.serial.StructReader;
 import net.sf.briar.api.serial.Reader;
 
 // This class is not thread-safe
@@ -23,7 +22,6 @@ class ReaderImpl implements Reader {
 	private final InputStream in;
 	private final Collection<Consumer> consumers = new ArrayList<Consumer>(0);
 
-	private StructReader<?>[] structReaders = new StructReader<?>[] {};
 	private boolean hasLookahead = false, eof = false;
 	private byte next, nextNext;
 	private byte[] buf = null;
@@ -98,24 +96,6 @@ class ReaderImpl implements Reader {
 		if(!consumers.remove(c)) throw new IllegalArgumentException();
 	}
 
-	public void addStructReader(int id, StructReader<?> r) {
-		if(id < 0 || id > 255) throw new IllegalArgumentException();
-		if(structReaders.length < id + 1) {
-			int len = Math.min(256, Math.max(id + 1, structReaders.length * 2));
-			StructReader<?>[] newStructReaders = new StructReader<?>[len];
-			System.arraycopy(structReaders, 0, newStructReaders, 0,
-					structReaders.length);
-			structReaders = newStructReaders;
-		}
-		structReaders[id] = r;
-	}
-
-	public void removeStructReader(int id) {
-		if(id < 0 || id > structReaders.length)
-			throw new IllegalArgumentException();
-		structReaders[id] = null;
-	}
-
 	public boolean hasBoolean() throws IOException {
 		if(!hasLookahead) readLookahead(true);
 		if(eof) return false;
@@ -371,7 +351,6 @@ class ReaderImpl implements Reader {
 	}
 
 	private Object readObject() throws IOException {
-		if(hasStruct()) return readStruct();
 		if(hasBoolean()) return Boolean.valueOf(readBoolean());
 		if(hasUint7()) return Byte.valueOf(readUint7());
 		if(hasInt8()) return Byte.valueOf(readInt8());
@@ -391,21 +370,6 @@ class ReaderImpl implements Reader {
 		throw new FormatException();
 	}
 
-	private boolean hasStruct() throws IOException {
-		if(!hasLookahead) readLookahead(true);
-		if(eof) return false;
-		return next == Tag.STRUCT
-				|| (next & Tag.SHORT_STRUCT_MASK) == Tag.SHORT_STRUCT;
-	}
-
-	private Object readStruct() throws IOException {
-		if(!hasStruct()) throw new FormatException();
-		int id;
-		if(next == Tag.STRUCT) id = 0xFF & nextNext;
-		else id = 0xFF & next ^ Tag.SHORT_STRUCT;
-		return readStruct(id, Object.class);
-	}
-
 	private <T> T readObject(Class<T> t) throws IOException {
 		try {
 			Object o = readObject();
@@ -529,18 +493,6 @@ class ReaderImpl implements Reader {
 		else return false;
 	}
 
-	public <T> T readStruct(int id, Class<T> t) throws IOException {
-		if(!hasStruct(id)) throw new FormatException();
-		if(id < 0 || id >= structReaders.length) throw new FormatException();
-		StructReader<?> s = structReaders[id];
-		if(s == null) throw new FormatException();
-		try {
-			return t.cast(s.readStruct(this));
-		} catch(ClassCastException e) {
-			throw new FormatException();
-		}
-	}
-
 	public void readStructId(int id) throws IOException {
 		if(!hasStruct(id)) throw new FormatException();
 		consumeLookahead();
diff --git a/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java b/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java
index a79cdb56b8661484bea66ae1f1e8664876cd7513..dc7382d9c15d488fbd33427b0040dc68f7cbe3bf 100644
--- a/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java
+++ b/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java
@@ -3,8 +3,6 @@ package net.sf.briar.serial;
 import static org.junit.Assert.assertArrayEquals;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -13,9 +11,6 @@ import java.util.Map.Entry;
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.Bytes;
 import net.sf.briar.api.FormatException;
-import net.sf.briar.api.serial.Consumer;
-import net.sf.briar.api.serial.StructReader;
-import net.sf.briar.api.serial.Reader;
 import net.sf.briar.util.StringUtils;
 
 import org.junit.Test;
@@ -370,135 +365,6 @@ public class ReaderImplTest extends BriarTestCase {
 		assertTrue(r.eof());
 	}
 
-	@Test
-	public void testReadStruct() throws Exception {
-		setContents("C0" + "83666F6F" + "F1" + "FF" + "83666F6F");
-		// Add readers for two structs
-		r.addStructReader(0, new StructReader<Foo>() {
-			public Foo readStruct(Reader r) throws IOException {
-				r.readStructId(0);
-				return new Foo(r.readString());
-			}
-		});
-		r.addStructReader(255, new StructReader<Bar>() {
-			public Bar readStruct(Reader r) throws IOException {
-				r.readStructId(255);
-				return new Bar(r.readString());
-			}
-		});
-		// Test both ID formats, short and long
-		assertTrue(r.hasStruct(0));
-		assertEquals("foo", r.readStruct(0, Foo.class).s);
-		assertTrue(r.hasStruct(255));
-		assertEquals("foo", r.readStruct(255, Bar.class).s);
-	}
-
-	@Test
-	public void testReadStructWithConsumer() throws Exception {
-		setContents("C0" + "83666F6F" + "F1" + "FF" + "83666F6F");
-		// Add readers for two structs
-		r.addStructReader(0, new StructReader<Foo>() {
-			public Foo readStruct(Reader r) throws IOException {
-				r.readStructId(0);
-				return new Foo(r.readString());
-			}
-		});
-		r.addStructReader(255, new StructReader<Bar>() {
-			public Bar readStruct(Reader r) throws IOException {
-				r.readStructId(255);
-				return new Bar(r.readString());
-			}
-		});
-		// Add a consumer
-		final ByteArrayOutputStream out = new ByteArrayOutputStream();
-		r.addConsumer(new Consumer() {
-
-			public void write(byte b) throws IOException {
-				out.write(b);
-			}
-
-			public void write(byte[] b, int off, int len) throws IOException {
-				out.write(b, off, len);
-			}
-		});
-		// Test both ID formats, short and long
-		assertTrue(r.hasStruct(0));
-		assertEquals("foo", r.readStruct(0, Foo.class).s);
-		assertTrue(r.hasStruct(255));
-		assertEquals("foo", r.readStruct(255, Bar.class).s);
-		// Check that everything was passed to the consumer
-		assertEquals("C0" + "83666F6F" + "F1" + "FF" + "83666F6F",
-				StringUtils.toHexString(out.toByteArray()));
-	}
-
-	@Test
-	public void testUnknownStructIdThrowsFormatException() throws Exception {
-		setContents("C0" + "83666F6F");
-		assertTrue(r.hasStruct(0));
-		// No reader has been added for struct ID 0
-		try {
-			r.readStruct(0, Foo.class);
-			fail();
-		} catch(FormatException expected) {}
-	}
-
-	@Test
-	public void testWrongClassThrowsFormatException() throws Exception {
-		setContents("C0" + "83666F6F");
-		// Add a reader for struct ID 0, class Foo
-		r.addStructReader(0, new StructReader<Foo>() {
-			public Foo readStruct(Reader r) throws IOException {
-				r.readStructId(0);
-				return new Foo(r.readString());
-			}
-		});
-		assertTrue(r.hasStruct(0));
-		// Trying to read the struct as class Bar should throw a FormatException
-		try {
-			r.readStruct(0, Bar.class);
-			fail();
-		} catch(FormatException expected) {}
-	}
-
-	@Test
-	public void testReadListUsingStructReader() throws Exception {
-		setContents("A" + "1" + "C0" + "83666F6F");
-		// Add a reader for a struct
-		r.addStructReader(0, new StructReader<Foo>() {
-			public Foo readStruct(Reader r) throws IOException {
-				r.readStructId(0);
-				return new Foo(r.readString());
-			}
-		});
-		// Check that the reader is used for lists
-		List<Foo> l = r.readList(Foo.class);
-		assertEquals(1, l.size());
-		assertEquals("foo", l.get(0).s);
-	}
-
-	@Test
-	public void testReadMapUsingStructReader() throws Exception {
-		setContents("B" + "1" + "C0" + "83666F6F" + "C1" + "83626172");
-		// Add readers for two structs
-		r.addStructReader(0, new StructReader<Foo>() {
-			public Foo readStruct(Reader r) throws IOException {
-				r.readStructId(0);
-				return new Foo(r.readString());
-			}
-		});
-		r.addStructReader(1, new StructReader<Bar>() {
-			public Bar readStruct(Reader r) throws IOException {
-				r.readStructId(1);
-				return new Bar(r.readString());
-			}
-		});
-		// Check that the readers are used for maps
-		Map<Foo, Bar> m = r.readMap(Foo.class, Bar.class);
-		assertEquals(1, m.size());
-		Entry<Foo, Bar> e = m.entrySet().iterator().next();
-		assertEquals("foo", e.getKey().s);
-		assertEquals("bar", e.getValue().s);
-	}
 
 	@Test
 	public void testMaxLengthAppliesInsideMap() throws Exception {
@@ -535,22 +401,4 @@ public class ReaderImplTest extends BriarTestCase {
 		in = new ByteArrayInputStream(StringUtils.fromHexString(hex));
 		r = new ReaderImpl(in);
 	}
-
-	private static class Foo {
-
-		private final String s;
-
-		private Foo(String s) {
-			this.s = s;
-		}
-	}
-
-	private static class Bar {
-
-		private final String s;
-
-		private Bar(String s) {
-			this.s = s;
-		}
-	}
 }