diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java
index ff351afcb4ad7f3a2c86760e719c20a69d73aeb3..1ad731facf94f00145337fc240fb207256c8dd62 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java
@@ -17,11 +17,11 @@ import tdt4140.gr1800.app.doc.IDocumentLoader;
 import tdt4140.gr1800.app.doc.IDocumentPersistence;
 import tdt4140.gr1800.app.doc.IDocumentStorage;
 import tdt4140.gr1800.app.gpx.GpxDocumentConverter;
-import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
+import tdt4140.gr1800.app.json.GeoLocationsPersistence;
 
 public class App {
 
-	private GeoLocationsStreamPersistence geoLocationsPersistence = new GeoLocationsJsonPersistence();
+	private GeoLocationsStreamPersistence geoLocationsPersistence = new GeoLocationsPersistence();
 	
 	private Collection<GeoLocations> geoLocations = null;
 
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java
index 5c6d552702fccff1ef5acf04e9f15a00e1224cd3..82075bc204491feeee0680d45f1a99c13dc93b1e 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java
@@ -18,6 +18,10 @@ public class GeoLocation extends TimedTaggedImpl implements GeoLocated, Timed, T
 		return elevation;
 	}
 	
+	public void setElevation(int elevation) {
+		this.elevation = elevation;
+	}
+	
 	private String name, description;
 	
 	public String getName() {
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java
index c4e335f698d4a694cf82976e313f4aec6dfaeda9..2281c980afd965e211dd6f4d5b0a1ae1f5863910 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java
@@ -15,53 +15,61 @@ public abstract class AbstractDbAccessImpl implements IDbAccess {
 	protected IdMap<GeoLocations> geoLocationsIds = new IdMap<GeoLocations>();
 	protected IdMap<GeoLocation> geoLocationIds = new IdMap<GeoLocation>();
 
-	public int getId(Person person) {
+	public IdProvider<Person> getPersonIdProvider() {
+		return personIds;
+	}
+
+	public int getId(final Person person) {
 		return personIds.getId(person);
 	}
 
-	public int getId(GeoLocations geoLocations) {
+	public int getId(final GeoLocations geoLocations) {
 		return geoLocationsIds.getId(geoLocations);
 	}
 
-	public int getId(GeoLocation geoLocation) {
+	public int getId(final GeoLocation geoLocation) {
 		return geoLocationIds.getId(geoLocation);
 	}
-	
+
 	@Override
-	public Person createPerson(String name, String email) {
-		Person person = new Person();
+	public Person createPerson(final String name, final String email) {
+		final Person person = new Person();
 		person.setName(name);
 		person.setEmail(email);
 		return person;
 	}
 
 	@Override
-	public GeoLocations createGeoLocations(Person owner) {
-		GeoLocations geoLocations = new GeoLocations(owner);
+	public GeoLocations createGeoLocations(final Person owner) {
+		final GeoLocations geoLocations = new GeoLocations(owner);
 		owner.addGeolocations(geoLocations);
 		return geoLocations;
 	}
 
 	@Override
-	public GeoLocation addGeoLocation(GeoLocations geoLocations, GeoLocated geoLoc, int elevation, LocalTime time) {
-		// TODO Auto-generated method stub
-		return null;
+	public GeoLocation addGeoLocation(final GeoLocations geoLocations, final GeoLocated geoLoc, final int elevation, final LocalTime time) {
+		final GeoLocation geoLocation = new GeoLocation();
+		geoLocation.setLatLong(geoLoc.getLatLong());
+		geoLocation.setElevation(elevation);
+		geoLocation.setTime(time);
+		geoLocations.addLocation(geoLoc);
+		return geoLocation;
 	}
 
 	@Override
-	public Collection<Person> getAllPersons(boolean refresh) {
+	public Collection<Person> getAllPersons(final boolean refresh) {
 		return new ArrayList<Person>(personIds.get());
 	}
 
 	@Override
-	public Person getPerson(int id, boolean refresh) {
-		Person person = personIds.get(id);
+	public Person getPerson(final int id, final boolean refresh) {
+		final Person person = personIds.get(id);
 		return person;
 	}
 
 	@Override
-	public Person getPersonByName(String name, boolean refresh) {
-		for (Person person : personIds.get()) {
+	public Person getPersonByName(final String name, final boolean refresh) {
+		for (final Person person : personIds.get()) {
 			if (name == person.getName() || (name != null && name.equals(person.getName()))) {
 				return person;
 			}
@@ -70,8 +78,8 @@ public abstract class AbstractDbAccessImpl implements IDbAccess {
 	}
 
 	@Override
-	public Person getPersonByEmail(String email, boolean refresh) {
-		for (Person person : personIds.get()) {
+	public Person getPersonByEmail(final String email, final boolean refresh) {
+		for (final Person person : personIds.get()) {
 			if (email == person.getEmail() || (email != null && email.equals(person.getEmail()))) {
 				return person;
 			}
@@ -80,41 +88,23 @@ public abstract class AbstractDbAccessImpl implements IDbAccess {
 	}
 
 	@Override
-	public Collection<GeoLocations> getGeoLocations(Person owner, boolean refresh) {
-		Collection<GeoLocations> result = new ArrayList<>();
-		for (GeoLocations geoLocations : geoLocationsIds.get()) {
-			if (geoLocations.getOwner() == owner) {
-				result.add(geoLocations);
-			}
-		}
-		return result;
-	}
-
-	@Override
-	public void updateGeoLocationsData(GeoLocations geoLocations) {
-		// TODO Auto-generated method stub
-		
-	}
-
-	@Override
-	public void updateGeoLocationData(GeoLocations geoLocations, GeoLocations geoLocation) {
-		// TODO Auto-generated method stub
-		
+	public Collection<GeoLocations> getGeoLocations(final Person owner, final boolean refresh) {
+		return owner.getGeoLocations((String[]) null);
 	}
 
 	@Override
-	public void deletePerson(Person person) {
+	public void deletePerson(final Person person) {
 		personIds.remove(person);
 	}
 
 	@Override
-	public void deleteGeoLocations(GeoLocations geoLocations) {
+	public void deleteGeoLocations(final GeoLocations geoLocations) {
 		geoLocations.getOwner().removeGeolocations(geoLocations);
 		geoLocationsIds.remove(geoLocations);
 	}
 
 	@Override
-	public void deleteGeoLocation(GeoLocations geoLocations, GeoLocation geoLocation) {
+	public void deleteGeoLocation(final GeoLocations geoLocations, final GeoLocation geoLocation) {
 		geoLocationIds.remove(geoLocation);
 	}
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java
index 0b6c0542d2852815d7b188651673646c2a2fac60..4435e4b628f34de613b002a87d4f142a180e4385 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java
@@ -1,13 +1,16 @@
 package tdt4140.gr1800.app.db;
 
 import java.sql.Connection;
+import java.sql.Date;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.SQLType;
 import java.sql.Statement;
+import java.sql.Time;
 import java.sql.Types;
+import java.time.LocalDate;
+import java.time.LocalTime;
 
 public class DbAccessHelper {
 
@@ -64,24 +67,37 @@ public class DbAccessHelper {
 		try {
 			preparedStatement = dbConnection.prepareStatement(statement);
 			for (int argNum = 1; argNum <= args.length; argNum++) {
-				Object arg = args[argNum - 1];
-				if (arg == null) {					
-					preparedStatement.setNull(argNum, Types.VARCHAR);
-				} else if (arg instanceof String)  {
-					preparedStatement.setString(argNum, (String) arg);
-				} else if (arg instanceof Double) {
-					preparedStatement.setDouble(argNum, (Double) arg);
-				} else if (arg instanceof Integer) {
-					preparedStatement.setInt(argNum, (Integer) arg);
-				} else if (arg instanceof Boolean) {
-					preparedStatement.setBoolean(argNum, (Boolean) arg);
-				}
+				setStatementArgument(preparedStatement, args[argNum - 1], null, argNum);
 			}
 		} catch (SQLException e) {
 			throwException(e);
 		}
 		return preparedStatement;
 	}
+	
+	protected void setStatementArgument(PreparedStatement preparedStatement, Object arg, Class<?> type, int argNum) throws SQLException {
+		if (arg == null) {
+			int sqlType = Types.VARCHAR;
+			if 		  (type == String.class)  	{ sqlType = Types.VARCHAR;
+			} else if (type == Double.class) 	{ sqlType = Types.DECIMAL;
+			} else if (type == Integer.class) 	{ sqlType = Types.INTEGER;
+			} else if (type == Boolean.class) 	{ sqlType = Types.BOOLEAN;
+			} else if (type == Date.class) 		{ sqlType = Types.DATE;
+			} else if (type == LocalDate.class) 	{ sqlType = Types.DATE;
+			} else if (type == Time.class) 		{ sqlType = Types.TIME;
+			} else if (type == LocalTime.class) 	{ sqlType = Types.TIME;
+			}
+			preparedStatement.setNull(argNum, sqlType);
+		} else if (arg instanceof String 	|| type == String.class)  	{ preparedStatement.setString(argNum, (String) arg);
+		} else if (arg instanceof Double 	|| type == Double.class) 	{ preparedStatement.setDouble(argNum, (Double) arg);
+		} else if (arg instanceof Integer 	|| type == Integer.class) 	{ preparedStatement.setInt	(argNum, (Integer) arg);
+		} else if (arg instanceof Boolean 	|| type == Boolean.class) 	{ preparedStatement.setBoolean(argNum, (Boolean) arg);
+		} else if (arg instanceof Date 		|| type == Date.class) 		{ preparedStatement.setDate	(argNum, (Date) arg);
+		} else if (arg instanceof LocalDate 	|| type == LocalDate.class) 	{ preparedStatement.setDate	(argNum, Date.valueOf((LocalDate) arg));
+		} else if (arg instanceof Time 		|| type == Time.class) 		{ preparedStatement.setTime	(argNum, (Time) arg);
+		} else if (arg instanceof LocalTime 	|| type == LocalTime.class) 	{ preparedStatement.setTime	(argNum, Time.valueOf((LocalTime) arg));
+		}
+	}
 
 	protected void executeDbStatement(String statement, Object... args) {
 		PreparedStatement preparedStatement = prepareStatement(statement, args);
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java
index 88f3b905a2a2dfe33d658b50c7a60049be42f452..4122a67e3e89bb899798658b05f089e829b5bf84 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java
@@ -1,11 +1,17 @@
 package tdt4140.gr1800.app.db;
 
 import java.sql.Connection;
+import java.sql.Date;
 import java.sql.DriverManager;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Time;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
 import java.util.Collection;
 
+import tdt4140.gr1800.app.core.GeoLocation;
 import tdt4140.gr1800.app.core.GeoLocations;
 import tdt4140.gr1800.app.core.Person;
 
@@ -166,18 +172,23 @@ public class DbAccessImpl extends AbstractDbAccessImpl {
 			geoLocationsIds.removeAll(existingGeoLocations);
 			existingGeoLocations.clear();
 			int ownerId = getId(owner);
-			ResultSet result = helper.executeQuery("SELECT id, path, name, description, date, time FROM geoLocations WHERE ownerId = ?", ownerId);
+			ResultSet result = helper.executeQuery("SELECT id, path, name, description, date, time, zone FROM geoLocations WHERE ownerId = ?", ownerId);
 			try {
 				while (result.next()) {
 					int id = result.getInt(1);
 					boolean path = result.getBoolean(2);
-					String name = result.getString(3), description = result.getString(3);
+					String name = result.getString(3), description = result.getString(4);
 					GeoLocations geoLocations = new GeoLocations(owner);
 					owner.addGeolocations(geoLocations);
 					geoLocations.setPath(path);
 					geoLocations.setName(name);
 					geoLocations.setDescription(description);
-					// TODO: date and time
+					Date date = result.getDate(5);
+					Time time = result.getTime(6);
+					String zone = result.getString(7);
+					geoLocations.setDate(date != null ? date.toLocalDate() : null);
+					geoLocations.setTime(time != null ? time.toLocalTime() : null);
+					geoLocations.setZone(zone != null ? ZoneId.of(zone) : null);
 					existingGeoLocations.add(geoLocations);
 					geoLocationsIds.set(geoLocations, id);
 					ResultSet tagResults = helper.executeQuery(String.format("SELECT tag FROM tag WHERE ownerId = ? AND ownerType = '%s'", TagOwnerType.GLS), id);
@@ -209,8 +220,11 @@ public class DbAccessImpl extends AbstractDbAccessImpl {
 	public void updateGeoLocationsData(GeoLocations geoLocations) {
 		boolean path = geoLocations.isPath();
 		String name = geoLocations.getName(), desc = geoLocations.getDescription();
+		LocalDate date = geoLocations.getDate();
+		LocalTime time = geoLocations.getTime();
+		String zone = (geoLocations.getZone() != null ? geoLocations.getZone().getId() : null);
 		int ownerId = getId(geoLocations);
-		helper.executeDbStatement("UPDATE geoLocations SET path = ?, name = ?, description = ? WHERE id = ?", path, name, desc, ownerId);
+		helper.executeDbStatement("UPDATE geoLocations SET path = ?, name = ?, description = ?, date = ?, time = ?, zone = ? WHERE id = ?", path, name, desc, Date.valueOf(date), Time.valueOf(time), zone, ownerId);
 		deleteTags(ownerId, TagOwnerType.GLS);
 		String insertStatement = "INSERT INTO tag (ownerId, ownerType, tag) VALUES ";
 		String[] tags = geoLocations.getTags();
@@ -234,4 +248,11 @@ public class DbAccessImpl extends AbstractDbAccessImpl {
 		helper.executeDbStatement("DELETE FROM geoLocations WHERE id = ?", ownerId);
 		deleteTags(ownerId, TagOwnerType.GLS);
 	}
+	
+	//
+
+	@Override
+	public void updateGeoLocationData(GeoLocations geoLocations, GeoLocation geoLocation) {
+		throw new UnsupportedOperationException("NYI");
+	}
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java
index 1a0be1aba4cac9226b2677f26ba6d33c9290cb1f..f95822761946826245501bdc18f8362b291da391 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java
@@ -6,12 +6,11 @@ import java.util.Collection;
 import tdt4140.gr1800.app.core.GeoLocated;
 import tdt4140.gr1800.app.core.GeoLocation;
 import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsOwner;
 import tdt4140.gr1800.app.core.Person;
 
 /*
  * CRUD interface for our domain:
- * 
+ *
  * @startuml
  * class GeoLocationsOwner {
  * 	String id
@@ -37,7 +36,9 @@ import tdt4140.gr1800.app.core.Person;
  * @enduml
  */
 public interface IDbAccess {
-	
+
+	public IdProvider<Person> getPersonIdProvider();
+
 	// Create
 	public Person createPerson(String name, String email);
 	public GeoLocations createGeoLocations(Person owner);
@@ -48,14 +49,14 @@ public interface IDbAccess {
 	public Person getPerson(int id, boolean refresh);
 	public Person getPersonByName(String name, boolean refresh);
 	public Person getPersonByEmail(String email, boolean refresh);
-	
+
 	public Collection<GeoLocations> getGeoLocations(Person owner, boolean refresh);
-	
+
 	// Update
 	public void updatePersonData(Person person);
 	public void updateGeoLocationsData(GeoLocations geoLocations);
-	public void updateGeoLocationData(GeoLocations geoLocations, GeoLocations geoLocation);
-	
+	public void updateGeoLocationData(GeoLocations geoLocations, GeoLocation geoLocation);
+
 	// Delete
 	public void deletePerson(Person person);
 	public void deleteGeoLocations(GeoLocations geoLocations);
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java
index e83e889afdbb13c7aca0142ed333826fd35f53ab..7eb2a5b2afad9d90fd4a5d14a185cc498e02ca0e 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java
@@ -4,31 +4,41 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
-public class IdMap<T> {
+public class IdMap<T> implements IdProvider<T> {
 
 	protected Map<Integer, T> id2o = new HashMap<Integer, T>();
 	protected Map<T, Integer> o2id = new HashMap<T, Integer>();
 
-	public T get(int id) {
+	public int size() {
+		return o2id.size();
+	}
+
+	public T get(final int id) {
 		return id2o.get(id);
 	}
 
-	public int getId(T o) {
-		Integer id = o2id.get(o);
+	@Override
+	public boolean hasId(final T t) {
+		return o2id.containsKey(t);
+	}
+
+	@Override
+	public int getId(final T o) {
+		final Integer id = o2id.get(o);
 		return (id != null ? id : -1);
 	}
-	
+
 	public Collection<T> get() {
 		return o2id.keySet();
 	}
-	
+
 	public Collection<Integer> getIds() {
 		return id2o.keySet();
 	}
 
 	//
-	
-	void set(T o, int id) {
+
+	public void set(final T o, final int id) {
 		if (o2id.containsKey(o)) {
 			throw new IllegalStateException(o + " already has the id " + o2id.get(o));
 		}
@@ -38,32 +48,32 @@ public class IdMap<T> {
 		id2o.put(id, o);
 		o2id.put(o, id);
 	}
-	
-	void clear() {
+
+	public void clear() {
 		o2id.clear();
-		id2o.clear();		
+		id2o.clear();
 	}
-	
-	private void remove(T o, int id) {
+
+	private void remove(final T o, final int id) {
 		o2id.remove(o);
-		id2o.remove(id);		
+		id2o.remove(id);
 	}
 
-	void remove(T o) {
+	public void remove(final T o) {
 		if (o2id.containsKey(o)) {
 			remove(o, o2id.get(o));
 		}
 	}
 
-	void removeAll(Iterable<T> os) {
-		for (T o : os) {
+	public void removeAll(final Iterable<T> os) {
+		for (final T o : os) {
 			remove(o);
 		}
 	}
 
-	void remove(int id) {
+	public void remove(final int id) {
 		if (id2o.containsKey(id)) {
 			remove(id2o.get(id), id);
-		}		
+		}
 	}
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdProvider.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f3af84efa972a82b15472dbc27ab6a8f1b33c5b
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdProvider.java
@@ -0,0 +1,6 @@
+package tdt4140.gr1800.app.db;
+
+public interface IdProvider<T> {
+	public boolean hasId(T t);
+	public int getId(T t);
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java
index e8c65d0a5edc1fceb409147d4d53b12c83aadde3..a019497879322f734c82499cbbde94892c641756 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java
@@ -50,7 +50,7 @@ public class GpxDocumentConverter implements IDocumentLoader<Collection<GeoLocat
 	public void setRouteCountFormat(String routeCountFormat) {
 		this.routeCountFormat = routeCountFormat;
 	}
-	
+
 	public Collection<GeoLocations> convert(GPX gpx) throws Exception {
 		Collection<GeoLocations> geoLocations = new ArrayList<GeoLocations>();
 		int trackCount = 1;
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocatedSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocatedSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bf5acfea6682c076f751d642b16a00067919597
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocatedSerializer.java
@@ -0,0 +1,31 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import tdt4140.gr1800.app.core.GeoLocated;
+
+public class GeoLocatedSerializer<T extends GeoLocated> extends StdSerializer<T> {
+	
+	public static final String LONGITUDE_FIELD_NAME = "longitude";
+	public static final String LATITUDE_FIELD_NAME = "latitude";
+
+	public GeoLocatedSerializer(Class<T> clazz) {
+		super(clazz);
+	}
+
+	protected void serialize(GeoLocated geoLocated, JsonGenerator jsonGen) throws IOException {
+		jsonGen.writeFieldName(LATITUDE_FIELD_NAME);
+		jsonGen.writeNumber(geoLocated.getLatitude());
+		jsonGen.writeFieldName(LONGITUDE_FIELD_NAME);
+		jsonGen.writeNumber(geoLocated.getLongitude());
+	}
+
+	@Override
+	public void serialize(T value, JsonGenerator jsonGen, SerializerProvider provider) throws IOException {
+		serialize(value, jsonGen);
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..c728cf14a97c5906b07db9e72a882d09035997a1
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationDeserializer.java
@@ -0,0 +1,62 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.GeoLocation;
+import tdt4140.gr1800.app.core.GeoLocations;
+
+public class GeoLocationDeserializer extends StdDeserializer<GeoLocation> {
+
+	public GeoLocationDeserializer() {
+		super(GeoLocations.class);
+	}
+
+	@Override
+	public GeoLocation deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException {
+		final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
+		return deserialize(jsonNode);
+	}
+
+	private final TimedTaggedImplDeserializer timedTaggedImplDeserializer = new TimedTaggedImplDeserializer();
+	private final LatLongDeserializer latLongDeserializer = new LatLongDeserializer();
+
+	GeoLocation deserialize(final JsonNode jsonNode) throws JsonProcessingException {
+		if (jsonNode instanceof ObjectNode) {
+			final ObjectNode objectNode = (ObjectNode) jsonNode;
+			final GeoLocation geoLocation = new GeoLocation();
+			if (objectNode.has(GeoLocationsSerializer.NAME_FIELD_NAME)) {
+				final String name = objectNode.get(GeoLocationsSerializer.NAME_FIELD_NAME).asText();
+				geoLocation.setName(name);
+			}
+			if (objectNode.has(GeoLocationsSerializer.DESCRIPTION_FIELD_NAME)) {
+				final String description = objectNode.get(GeoLocationsSerializer.DESCRIPTION_FIELD_NAME).asText();
+				geoLocation.setDescription(description);
+			}
+			if (objectNode.has("location")) {
+				final JsonNode locationNode = objectNode.get("location");
+				geoLocation.setLatLong(latLongDeserializer.deserialize(locationNode));
+			} else {
+				geoLocation.setLatLong(latLongDeserializer.deserialize(objectNode));
+			}
+			if (objectNode.has(GeoLocationSerializer.ELEVATION_FIELD_NAME)) {
+				final int elevation = objectNode.get(GeoLocationSerializer.ELEVATION_FIELD_NAME).asInt();
+				geoLocation.setElevation(elevation);
+			}
+			timedTaggedImplDeserializer.deserialize(jsonNode, geoLocation);
+			return geoLocation;
+		} else if (jsonNode instanceof ArrayNode) {
+			final GeoLocation geoLocation = new GeoLocation();
+			geoLocation.setLatLong(latLongDeserializer.deserialize(jsonNode));
+			return geoLocation;
+		}
+		return null;
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..304c52816f462070384876611dfa01ac44fd1563
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationSerializer.java
@@ -0,0 +1,39 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import tdt4140.gr1800.app.core.GeoLocation;
+
+public class GeoLocationSerializer extends TimedTaggedSerializer<GeoLocation> {
+	
+	public static final String ELEVATION_FIELD_NAME = "elevation";
+
+	public GeoLocationSerializer() {
+		super(GeoLocation.class);
+	}
+	
+	private GeoLocatedSerializer<GeoLocation> geoLocatedSerializer = new GeoLocatedSerializer<>(GeoLocation.class);
+
+	@Override
+	public void serialize(GeoLocation geoLocation, JsonGenerator jsonGen, SerializerProvider serProvider) throws IOException {
+		jsonGen.writeStartObject();
+		super.serialize(geoLocation, jsonGen);
+		geoLocatedSerializer.serialize(geoLocation, jsonGen);
+		if (geoLocation.getElevation() != 0) {
+			jsonGen.writeFieldName(ELEVATION_FIELD_NAME);
+			jsonGen.writeNumber(geoLocation.getElevation());
+		}
+		if (geoLocation.getName() != null) {
+			jsonGen.writeFieldName(GeoLocationsSerializer.NAME_FIELD_NAME);
+			jsonGen.writeString(geoLocation.getName());
+		}
+		if (geoLocation.getDescription() != null) {
+			jsonGen.writeFieldName(GeoLocationsSerializer.DESCRIPTION_FIELD_NAME);
+			jsonGen.writeString(geoLocation.getDescription());
+		}
+		jsonGen.writeEndObject();
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc3a5137d5a466f9ce26066de4452eabec10e82a
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsDeserializer.java
@@ -0,0 +1,48 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.GeoLocation;
+import tdt4140.gr1800.app.core.GeoLocations;
+
+public class GeoLocationsDeserializer extends StdDeserializer<GeoLocations> {
+
+	public GeoLocationsDeserializer() {
+		super(GeoLocations.class);
+	}
+
+	@Override
+	public GeoLocations deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException {
+		final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
+		return deserialize(jsonNode, deserContext);
+	}
+
+	private final GeoLocationDeserializer geoLocationDeserializer = new GeoLocationDeserializer();
+
+	GeoLocations deserialize(final JsonNode jsonNode, final DeserializationContext deserContext) throws JsonProcessingException {
+		if (jsonNode instanceof ObjectNode) {
+			final ObjectNode objectNode = (ObjectNode) jsonNode;
+			final String name = objectNode.get(GeoLocationsSerializer.NAME_FIELD_NAME).asText();
+			final GeoLocations geoLocations = new GeoLocations(name);
+			final JsonNode pathNode = objectNode.get(GeoLocationsSerializer.PATH_FIELD_NAME);
+			geoLocations.setPath(pathNode != null && pathNode.asBoolean(false));
+			final JsonNode locationsNode = objectNode.get(GeoLocationsSerializer.LOCATIONS_FIELD_NAME);
+			if (locationsNode instanceof ArrayNode) {
+				for (final JsonNode locationNode : (ArrayNode) locationsNode) {
+					final GeoLocation geoLocation = geoLocationDeserializer.deserialize(locationNode);
+					geoLocations.addLocation(geoLocation);
+				}
+			}
+			return geoLocations;
+		}
+		return null;
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonDeserializer.java
deleted file mode 100644
index c5f0e3dd4e844e3eb6803d31181fd24d746ba2dd..0000000000000000000000000000000000000000
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonDeserializer.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package tdt4140.gr1800.app.json;
-
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.LatLong;
-
-public class GeoLocationsJsonDeserializer extends StdDeserializer<GeoLocations> {
-	
-	public GeoLocationsJsonDeserializer() {
-		super(GeoLocations.class);
-	}
-	
-	@Override
-	public GeoLocations deserialize(JsonParser jsonParser, DeserializationContext deserContext) throws IOException, JsonProcessingException {
-		JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
-		return deserialize(jsonNode);
-	}
-	
-	private GeoLocations deserialize(JsonNode jsonNode) throws IOException, JsonProcessingException {
-		if (jsonNode instanceof ObjectNode) {
-			ObjectNode objectNode = (ObjectNode) jsonNode;
-			String name = objectNode.get(GeoLocationsJsonSerializer.NAME_FIELD_NAME).asText();
-			GeoLocations geoLocations = new GeoLocations(name);
-			JsonNode pathNode = objectNode.get(GeoLocationsJsonSerializer.PATH_FIELD_NAME);
-			geoLocations.setPath(pathNode != null && pathNode.asBoolean(false));
-			JsonNode locationsNode = objectNode.get(GeoLocationsJsonSerializer.LOCATIONS_FIELD_NAME);
-			if (locationsNode instanceof ArrayNode) {
-				for (JsonNode locationNode : (ArrayNode) locationsNode) {
-					LatLong latLong = decodeLatLong(locationNode);
-					geoLocations.addLocation(latLong);
-				}
-			} else {
-				LatLong latLong = decodeLatLong(locationsNode);
-				geoLocations.addLocation(latLong);				
-			}
-			return geoLocations;
-		}
-		return null;
-	}
-
-	private LatLong decodeLatLong(JsonNode locationNode) {
-		LatLong latLong = null;
-		if (locationNode instanceof ObjectNode) {
-			ObjectNode objectNode = (ObjectNode) locationNode;
-			if (objectNode.has(GeoLocationsJsonSerializer.LATITUDE_FIELD_NAME) && objectNode.has(GeoLocationsJsonSerializer.LONGITUDE_FIELD_NAME)) {
-				double lat = objectNode.get(GeoLocationsJsonSerializer.LATITUDE_FIELD_NAME).asDouble();
-				double lon = objectNode.get(GeoLocationsJsonSerializer.LONGITUDE_FIELD_NAME).asDouble();
-				latLong = new LatLong(lat, lon);
-			}
-		} else if (locationNode instanceof ArrayNode) {
-			ArrayNode arrayNode = (ArrayNode) locationNode;
-			if (arrayNode.size() == 2) {
-				double lat = arrayNode.get(0).asDouble();
-				double lon = arrayNode.get(1).asDouble();
-				latLong = new LatLong(lat, lon);			
-			}
-		}
-		return latLong;
-	}
-}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java
deleted file mode 100644
index 66a078eca89a05489d52a219554a910463781b8f..0000000000000000000000000000000000000000
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package tdt4140.gr1800.app.json;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.List;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
-
-public class GeoLocationsJsonPersistence implements GeoLocationsStreamPersistence {
-
-	private final ObjectMapper objectMapper;
-
-	public GeoLocationsJsonPersistence() {
-		objectMapper = new ObjectMapper();
-		SimpleModule module = new SimpleModule();
-		module.addSerializer(new GeoLocationsJsonSerializer());
-		module.addDeserializer(GeoLocations.class, new GeoLocationsJsonDeserializer());
-		objectMapper.registerModule(module);
-	}
-	
-	@Override
-	public Collection<GeoLocations> loadLocations(InputStream inputStream) throws Exception {
-		return objectMapper.readValue(inputStream, objectMapper.getTypeFactory().constructCollectionType(List.class, GeoLocations.class));
-	}
-
-	@Override
-	public void saveLocations(Collection<GeoLocations> geoLocations, OutputStream outputStream) throws Exception {
-		objectMapper.writeValue(outputStream, geoLocations);
-	}
-}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsPersistence.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsPersistence.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ceb613910fa764ed2bef2ec6d70241f514bacb2
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsPersistence.java
@@ -0,0 +1,44 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import tdt4140.gr1800.app.core.GeoLocation;
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
+import tdt4140.gr1800.app.core.LatLong;
+import tdt4140.gr1800.app.core.Person;
+
+public class GeoLocationsPersistence implements GeoLocationsStreamPersistence {
+
+	private final ObjectMapper objectMapper;
+
+	public GeoLocationsPersistence() {
+		objectMapper = new ObjectMapper();
+		final SimpleModule module = new SimpleModule();
+		module.addSerializer(new LatLongSerializer());
+		module.addSerializer(new GeoLocationSerializer());
+		module.addSerializer(new GeoLocationsSerializer());
+		module.addSerializer(new PersonSerializer());
+		module.addDeserializer(LatLong.class, new LatLongDeserializer());
+		module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer());
+		module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer());
+		module.addDeserializer(Person.class, new PersonDeserializer());
+		objectMapper.registerModule(module);
+	}
+
+	@Override
+	public Collection<GeoLocations> loadLocations(final InputStream inputStream) throws Exception {
+		return objectMapper.readValue(inputStream, objectMapper.getTypeFactory().constructCollectionType(List.class, GeoLocations.class));
+	}
+
+	@Override
+	public void saveLocations(final Collection<GeoLocations> geoLocations, final OutputStream outputStream) throws Exception {
+		objectMapper.writeValue(outputStream, geoLocations);
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsSerializer.java
similarity index 66%
rename from tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonSerializer.java
rename to tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsSerializer.java
index 2f08083a2f4bea94a40612fc1357a0ef034a568b..276d6ce6f8e00189991e1817d481699f05ca5895 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonSerializer.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsSerializer.java
@@ -9,34 +9,37 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
 import tdt4140.gr1800.app.core.GeoLocated;
 import tdt4140.gr1800.app.core.GeoLocations;
 
-public class GeoLocationsJsonSerializer extends StdSerializer<GeoLocations> {
+public class GeoLocationsSerializer extends StdSerializer<GeoLocations> {
 	
-	public static final String LONGITUDE_FIELD_NAME = "longitude";
-	public static final String LATITUDE_FIELD_NAME = "latitude";
 	public static final String LOCATIONS_FIELD_NAME = "locations";
 	public static final String PATH_FIELD_NAME = "path";
 	public static final String NAME_FIELD_NAME = "name";
+	public static final String DESCRIPTION_FIELD_NAME = "description";
 
-	public GeoLocationsJsonSerializer() {
+	public GeoLocationsSerializer() {
 		super(GeoLocations.class);
 	}
 
+	private TimedTaggedSerializer<GeoLocations> timedTaggedSerializer = new TimedTaggedSerializer<>(GeoLocations.class);
+	
 	@Override
 	public void serialize(GeoLocations geoLocations, JsonGenerator jsonGen, SerializerProvider serProvider) throws IOException {
 		jsonGen.writeStartObject();
 		jsonGen.writeFieldName(NAME_FIELD_NAME);
 		jsonGen.writeString(geoLocations.getName());
+		if (geoLocations.getDescription() != null) {
+			jsonGen.writeFieldName(DESCRIPTION_FIELD_NAME);
+			jsonGen.writeString(geoLocations.getDescription());
+		}
 		jsonGen.writeFieldName(PATH_FIELD_NAME);
 		jsonGen.writeBoolean(geoLocations.isPath());
 		jsonGen.writeFieldName(LOCATIONS_FIELD_NAME);
 		jsonGen.writeStartArray();
 		for (GeoLocated geoLoc : geoLocations) {
-			jsonGen.writeStartArray();
-			jsonGen.writeNumber(geoLoc.getLatitude());
-			jsonGen.writeNumber(geoLoc.getLongitude());
-			jsonGen.writeEndArray();
+			jsonGen.writeObject(geoLoc);
 		}
 		jsonGen.writeEndArray();
+		timedTaggedSerializer.serialize(geoLocations, jsonGen);
 		jsonGen.writeEndObject();
 	}
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/IdSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/IdSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..3df8e3383890dbd780261bf50a692d46c8f7cd4f
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/IdSerializer.java
@@ -0,0 +1,30 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import tdt4140.gr1800.app.db.IdProvider;
+
+public abstract class IdSerializer<T> extends StdSerializer<T> {
+
+	public static final String ID_FIELD_NAME = "id";
+
+	public IdSerializer(final Class<T> clazz) {
+		super(clazz);
+	}
+
+	private IdProvider<T> idProvider = null;
+
+	public void setIdProvider(final IdProvider<T> idProvider) {
+		this.idProvider = idProvider;
+	}
+
+	protected void serializeId(final T t, final JsonGenerator jsonGen) throws IOException {
+		if (idProvider != null && idProvider.hasId(t)) {
+			jsonGen.writeFieldName(ID_FIELD_NAME);
+			jsonGen.writeNumber(idProvider.getId(t));
+		}
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f32025f4371d5e23acc2c842ae733173dbb7292
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongDeserializer.java
@@ -0,0 +1,43 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.LatLong;
+
+public class LatLongDeserializer extends StdDeserializer<LatLong> {
+
+	public LatLongDeserializer() {
+		super(LatLong.class);
+	}
+
+	@Override
+	public LatLong deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException {
+		final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
+		return deserialize(jsonNode);
+	}
+
+	public LatLong deserialize(final JsonNode jsonNode) throws JsonProcessingException {
+		if (jsonNode instanceof ObjectNode) {
+			final ObjectNode objectNode = (ObjectNode) jsonNode;
+			final double latitude = objectNode.get(GeoLocatedSerializer.LATITUDE_FIELD_NAME).asDouble();
+			final double longitude = objectNode.get(GeoLocatedSerializer.LONGITUDE_FIELD_NAME).asDouble();
+			return new LatLong(latitude, longitude);
+		} else if (jsonNode instanceof ArrayNode) {
+			final ArrayNode locationArray = (ArrayNode) jsonNode;
+			if (locationArray.size() == 2) {
+				final double latitude = locationArray.get(0).asDouble();
+				final double longitude = locationArray.get(1).asDouble();
+				return new LatLong(latitude, longitude);
+			}
+		}
+		return null;
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1d911417f7a7a089d11f468f95aed28299597e2
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongSerializer.java
@@ -0,0 +1,22 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import tdt4140.gr1800.app.core.LatLong;
+
+public class LatLongSerializer extends GeoLocatedSerializer<LatLong> {
+
+	public LatLongSerializer() {
+		super(LatLong.class);
+	}
+
+	@Override
+	public void serialize(LatLong geoLocated, JsonGenerator jsonGen, SerializerProvider serProvider) throws IOException {
+		jsonGen.writeStartObject();
+		super.serialize(geoLocated, jsonGen);
+		jsonGen.writeEndObject();
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c7f32a61139b4478af722e6cc3427969ce1686c
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonDeserializer.java
@@ -0,0 +1,51 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.Person;
+
+public class PersonDeserializer extends StdDeserializer<Person> {
+
+	public PersonDeserializer() {
+		super(Person.class);
+	}
+
+	@Override
+	public Person deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException {
+		final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
+		return deserialize(jsonNode, deserContext);
+	}
+
+	private final GeoLocationsDeserializer geoLocationsDeserializer = new GeoLocationsDeserializer();
+
+	Person deserialize(final JsonNode jsonNode, final DeserializationContext deserContext) throws JsonProcessingException {
+		if (jsonNode instanceof ObjectNode) {
+			final ObjectNode objectNode = (ObjectNode) jsonNode;
+			final Person person = new Person();
+			if (objectNode.has(GeoLocationsSerializer.NAME_FIELD_NAME)) {
+				final String name = objectNode.get(GeoLocationsSerializer.NAME_FIELD_NAME).asText();
+				person.setName(name);
+			}
+			if (objectNode.has(GeoLocationsSerializer.LOCATIONS_FIELD_NAME)) {
+				final JsonNode locationsNode = objectNode.get(GeoLocationsSerializer.LOCATIONS_FIELD_NAME);
+				if (locationsNode instanceof ArrayNode) {
+					for (final JsonNode childNode : ((ArrayNode) locationsNode)) {
+						final GeoLocations geoLocations = geoLocationsDeserializer.deserialize(childNode, deserContext);
+						person.addGeolocations(geoLocations);
+					}
+				}
+			}
+			return person;
+		}
+		return null;
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a556febbb96d81b037aaf9cb411d6748db121e1
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonSerializer.java
@@ -0,0 +1,40 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.Person;
+
+public class PersonSerializer extends IdSerializer<Person> {
+
+	public static final String ID_FIELD_NAME = "id";
+	public static final String NAME_FIELD_NAME = "name";
+	public static final String EMAIL_FIELD_NAME = "email";
+	public static final String GEOLOCATIONS_FIELD_NAME = "geoLocations";
+
+	public PersonSerializer() {
+		super(Person.class);
+	}
+
+	@Override
+	public void serialize(final Person person, final JsonGenerator jsonGen, final SerializerProvider serProvider) throws IOException {
+		jsonGen.writeStartObject();
+		serializeId(person, jsonGen);
+		jsonGen.writeFieldName(NAME_FIELD_NAME);
+		jsonGen.writeString(person.getName());
+		if (person.getEmail() != null) {
+			jsonGen.writeFieldName(EMAIL_FIELD_NAME);
+			jsonGen.writeString(person.getEmail());
+		}
+		jsonGen.writeFieldName(GEOLOCATIONS_FIELD_NAME);
+		jsonGen.writeStartArray();
+		for (final GeoLocations geoLocations : person.getGeoLocations((String[]) null)) {
+			jsonGen.writeObject(geoLocations);
+		}
+		jsonGen.writeEndArray();
+		jsonGen.writeEndObject();
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedImplDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedImplDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..66f68fdf0b7ab971110ae618aacd67fdcd636e12
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedImplDeserializer.java
@@ -0,0 +1,44 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.TimedTaggedImpl;
+
+public class TimedTaggedImplDeserializer extends StdDeserializer<TimedTaggedImpl> {
+
+	public TimedTaggedImplDeserializer() {
+		super(TimedTaggedImpl.class);
+	}
+
+	@Override
+	public TimedTaggedImpl deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException {
+		final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
+		return deserialize(jsonNode, new TimedTaggedImpl());
+	}
+
+	public TimedTaggedImpl deserialize(final JsonNode jsonNode, final TimedTaggedImpl timedTagged) throws JsonProcessingException {
+		if (jsonNode instanceof ObjectNode) {
+			final ObjectNode objectNode = (ObjectNode) jsonNode;
+			if (objectNode.has(TimedTaggedSerializer.DATE_FIELD_NAME)) {
+				timedTagged.setDate(LocalDate.parse(objectNode.get(TimedTaggedSerializer.DATE_FIELD_NAME).asText()));
+			}
+			if (objectNode.has(TimedTaggedSerializer.TIME_FIELD_NAME)) {
+				timedTagged.setTime(LocalTime.parse(objectNode.get(TimedTaggedSerializer.TIME_FIELD_NAME).asText()));
+			}
+			if (objectNode.has(TimedTaggedSerializer.ZONE_FIELD_NAME)) {
+				timedTagged.setZone(ZoneId.of(objectNode.get(TimedTaggedSerializer.ZONE_FIELD_NAME).asText()));
+			}
+		}
+		return timedTagged;
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..859fb39a5c7b9dc94d65b9d36b020f984299f542
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedSerializer.java
@@ -0,0 +1,46 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import tdt4140.gr1800.app.core.TimedTaggedImpl;
+
+public class TimedTaggedSerializer<T extends TimedTaggedImpl> extends StdSerializer<T> {
+	
+	public static final String DATE_FIELD_NAME = "date";
+	public static final String TIME_FIELD_NAME = "time";
+	public static final String ZONE_FIELD_NAME = "zone";
+	public static final String TAGS_FIELD_NAME = "tags";
+
+	public TimedTaggedSerializer(Class<T> clazz) {
+		super(clazz);
+	}
+
+	protected void serialize(TimedTaggedImpl timedTagged, JsonGenerator jsonGen) throws IOException {
+		if (timedTagged.getDate() != null) {
+			jsonGen.writeFieldName(DATE_FIELD_NAME);
+			jsonGen.writeString(timedTagged.getDate().toString());
+		}
+		if (timedTagged.getTime() != null) {
+			jsonGen.writeFieldName(TIME_FIELD_NAME);
+			jsonGen.writeString(timedTagged.getTime().toString());
+		}
+		String[] tags = timedTagged.getTags();
+		if (tags != null && tags.length > 0) {
+			jsonGen.writeFieldName(TAGS_FIELD_NAME);
+			jsonGen.writeStartArray();
+			for (int i = 0; i < tags.length; i++) {
+				jsonGen.writeString(tags[i]);
+			}
+			jsonGen.writeEndArray();
+		}
+	}
+
+	@Override
+	public void serialize(T value, JsonGenerator jsonGen, SerializerProvider provider) throws IOException {
+		serialize(value, jsonGen);
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql b/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql
index 321c749e8b043366e55297301b6bc7ce820ac08d..3b16fc7bc69be19be34addd3ff03e65985c1e984 100644
--- a/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql
+++ b/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql
@@ -21,6 +21,7 @@ CREATE TABLE geoLocations (
 
 	date date NULL,
 	time time NULL,
+	zone varchar(20) NULL,
 );
 
 CREATE TABLE geoLocation (
@@ -36,6 +37,7 @@ CREATE TABLE geoLocation (
 
 	date date NULL,
 	time time NULL,
+	zone varchar(20) NULL,
 );
 
 CREATE TABLE tag (
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java
index fc5e13c0da1df6808f2a142d304e164161f9242b..ab6e089a7ab4463e5b4519ac728c45edbaad21de 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java
@@ -9,6 +9,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 import tdt4140.gr1800.app.doc.IDocumentStorage;
+import tdt4140.gr1800.app.json.GeoLocationsStreamPersistenceTest;
 
 public class AppTest {
 
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java
deleted file mode 100644
index 2eaa8a872919dcf63b5bbb89f03cd0b4cbdfe53d..0000000000000000000000000000000000000000
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package tdt4140.gr1800.app.core;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
-
-public class GeoLocationsStreamPersistenceTest {
-
-	private GeoLocationsStreamPersistence persistence;
-	
-	@Before
-	public void setUp() {
-		persistence = new GeoLocationsJsonPersistence();
-	}
-	
-	@Test
-	public void testLoadLocations() {
-		try {
-			Collection<GeoLocations> geoLocations = persistence.loadLocations(getClass().getResourceAsStream("geoLocations.json"));
-			testGeoLocationsDotJson(geoLocations);
-		} catch (Exception e) {
-			Assert.fail(e.getMessage());
-		}
-	}
-	
-	public static Collection<GeoLocations> createGeoLocationsDotJson() {
-		return Arrays.asList(
-			new GeoLocations("1", new LatLong(63, 10), new LatLong(63.1, 10.1)),
-			new GeoLocations("2", new LatLong(64, 11), new LatLong(64.1, 11.1))
-		);
-	}
-	
-	public static void testGeoLocationsDotJson(Collection<GeoLocations> geoLocations) {
-		Assert.assertEquals(2, geoLocations.size());
-		Iterator<GeoLocations> it = geoLocations.iterator();
-		GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(63, 10), new LatLong(63.1, 10.1));
-		GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(64, 11), new LatLong(64.1, 11.1));
-	}
-
-	@Test
-	public void testSaveLocations() {
-		Collection<GeoLocations> geoLocations = createGeoLocationsDotJson();
-		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-		try {
-			persistence.saveLocations(geoLocations, outputStream);
-			outputStream.close();
-			Collection<GeoLocations> geoLocations2 = persistence.loadLocations(new ByteArrayInputStream(outputStream.toByteArray()));
-			testGeoLocationsDotJson(geoLocations2);
-		} catch (Exception e) {
-			Assert.fail(e.getMessage());
-		}
-	}
-}
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java
index c03b9e50612ce3a969a1a42e4c7957b29e3455c3..1081bb79c54449c557ec3b0000795cf980687e73 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java
@@ -10,7 +10,8 @@ import org.junit.Test;
 public class GeoLocationsTest extends TimedTaggedTest {
 
 	private GeoLocations geoLocations;
-	
+
+	@Override
 	@Before
 	public void setUp() {
 		setUp(geoLocations = new GeoLocations());
@@ -24,12 +25,12 @@ public class GeoLocationsTest extends TimedTaggedTest {
 		Assert.assertEquals(2, new GeoLocations(new LatLong(0, 0), new LatLong(1, 1)).size());
 	}
 
-	private void assertGeoLocations(LatLong...latLongs) {
+	private void assertGeoLocations(final LatLong...latLongs) {
 		assertGeoLocations(geoLocations, latLongs);
 	}
 
-	public static void assertGeoLocations(GeoLocations geoLocations, LatLong...latLongs) {
-		Iterator<GeoLocated> it = geoLocations.iterator();
+	public static void assertGeoLocations(final GeoLocations geoLocations, final LatLong...latLongs) {
+		final Iterator<GeoLocated> it = geoLocations.iterator();
 		Assert.assertEquals(latLongs.length, geoLocations.size());
 		int pos = 0;
 		while (it.hasNext()) {
@@ -38,8 +39,8 @@ public class GeoLocationsTest extends TimedTaggedTest {
 		}
 	}
 
-	public static void assertGeoLocations(GeoLocations geoLocations, GeoLocations geoLocations2) {
-		Iterator<GeoLocated> it = geoLocations.iterator(), it2 = geoLocations2.iterator();
+	public static void assertGeoLocations(final GeoLocations geoLocations, final GeoLocations geoLocations2) {
+		final Iterator<GeoLocated> it = geoLocations.iterator(), it2 = geoLocations2.iterator();
 		while (it.hasNext()) {
 			Assert.assertTrue(it2.hasNext());
 			checkGeoLocated(it2.next(), it.next());
@@ -50,50 +51,50 @@ public class GeoLocationsTest extends TimedTaggedTest {
 	@Test
 	public void testAddLocation() {
 		Assert.assertEquals(0, geoLocations.size());
-		LatLong latLong1 = new LatLong(0, 0);
+		final LatLong latLong1 = new LatLong(0, 0);
 		geoLocations.addLocation(latLong1);
 		assertGeoLocations(latLong1);
 
 		geoLocations.addLocation(new LatLong(0, 0));
 		assertGeoLocations(latLong1, latLong1);
 
-		LatLong latLong2 = new LatLong(1, 1);
+		final LatLong latLong2 = new LatLong(1, 1);
 		geoLocations.addLocation(latLong2);
 		assertGeoLocations(latLong1, latLong1, latLong2);
 	}
 
-	static void checkGeoLocated(GeoLocated geoLoc1, GeoLocated geoLoc2) {
-		Assert.assertTrue(geoLoc1.equalsLatLong(geoLoc2));
+	static void checkGeoLocated(final GeoLocated geoLoc1, final GeoLocated geoLoc2) {
+		Assert.assertTrue(geoLoc1.getLatLong().toString() + " expected, but was " + geoLoc2.getLatLong(), geoLoc1.equalsLatLong(geoLoc2));
 	}
-	
+
 	@Test
-	public void testFindLocationsNearby() { 
-		LatLong latLong = new LatLong(0, 0);
+	public void testFindLocationsNearby() {
+		final LatLong latLong = new LatLong(0, 0);
 		Assert.assertTrue(geoLocations.findLocationsNearby(latLong, 0).isEmpty());
 		geoLocations.addLocation(latLong);
-		Collection<GeoLocated> locationsNearby = geoLocations.findLocationsNearby(latLong, 0);
+		final Collection<GeoLocated> locationsNearby = geoLocations.findLocationsNearby(latLong, 0);
 		Assert.assertEquals(1, locationsNearby.size());
-		checkGeoLocated(latLong, geoLocations.iterator().next()); 
+		checkGeoLocated(latLong, geoLocations.iterator().next());
 	}
-	
+
 	@Test
 	public void testFindNearestLocation() {
-		LatLong latLong = new LatLong(0, 0);
+		final LatLong latLong = new LatLong(0, 0);
 		Assert.assertNull(geoLocations.findNearestLocation(latLong));
 		geoLocations.addLocation(latLong);
-		GeoLocated nearestlocations = geoLocations.findNearestLocation(latLong);
+		final GeoLocated nearestlocations = geoLocations.findNearestLocation(latLong);
 		checkGeoLocated(latLong, nearestlocations);
 	}
-	
+
 	@Test
 	public void testRemoveSameLocations() {
-		GeoLocations geoLocations1 = new GeoLocations(new LatLong(0, 0), new LatLong(1, 1));
+		final GeoLocations geoLocations1 = new GeoLocations(new LatLong(0, 0), new LatLong(1, 1));
 		Assert.assertEquals(2, geoLocations1.size());
 		geoLocations1.removeLocations(new LatLong(0, 0), 0.0);
 		Assert.assertEquals(1, geoLocations1.size());
 		geoLocations1.removeLocations(new LatLong(1, 1), 0.0);
-		Assert.assertEquals(0, geoLocations1.size());		
-		GeoLocations geoLocations2 = new GeoLocations(new LatLong(0, 0), new LatLong(0, 0));
+		Assert.assertEquals(0, geoLocations1.size());
+		final GeoLocations geoLocations2 = new GeoLocations(new LatLong(0, 0), new LatLong(0, 0));
 		Assert.assertEquals(2, geoLocations2.size());
 		geoLocations2.removeLocations(new LatLong(0, 0), 0.0);
 		Assert.assertEquals(0, geoLocations2.size());
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java
index 20c0f0b0f0acc5f0b324071d38f1b02bfca5808e..b585b22a67d23f26d82cd21b27a1ff7e138d4aee 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java
@@ -4,6 +4,8 @@ import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.time.LocalDate;
+import java.time.LocalTime;
 import java.util.Collection;
 import java.util.Scanner;
 
@@ -149,6 +151,10 @@ public class HsqldbAccessTest {
 		Person hal = dbAccess.createPerson("hal", "hal@ntnu.no");
 		GeoLocations geoLocations1 = dbAccess.createGeoLocations(hal);
 		geoLocations1.setName("geoLocs1");
+		geoLocations1.setDescription("my first geo-location");
+		geoLocations1.setDate(LocalDate.of(2018, 3, 14));
+		geoLocations1.setTime(LocalTime.of(11, 14));
+		geoLocations1.setZone("Europe/Oslo");
 		String[] tags = {"tag1", "tag2"}; 
 		geoLocations1.addTags(tags);
 		dbAccess.updateGeoLocationsData(geoLocations1);
@@ -158,6 +164,11 @@ public class HsqldbAccessTest {
 		Assert.assertEquals(1, hal.getGeoLocations((String[]) null).size()); 
 		Assert.assertEquals(1, dbGeoLocations.size());
 		GeoLocations dbGeoLocations1 = dbGeoLocations.iterator().next();
+		Assert.assertEquals(geoLocations1.getName(), dbGeoLocations1.getName());
+		Assert.assertEquals(geoLocations1.getDescription(), dbGeoLocations1.getDescription());
+		Assert.assertEquals(geoLocations1.getDate(), dbGeoLocations1.getDate());
+		Assert.assertEquals(geoLocations1.getTime(), dbGeoLocations1.getTime());
+		Assert.assertEquals(geoLocations1.getZone(), dbGeoLocations1.getZone());
 		Assert.assertEquals(2, dbGeoLocations1.getTags().length);
 		Assert.assertTrue(dbGeoLocations1.hasTags(tags));
 	}
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GpxPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/gpx/GpxPersistenceTest.java
similarity index 97%
rename from tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GpxPersistenceTest.java
rename to tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/gpx/GpxPersistenceTest.java
index 67a6fb87b40eec0f9e4cbba8b98875a593492a59..80b4db20d8094b21f95f45309da5c6a293cf6483 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GpxPersistenceTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/gpx/GpxPersistenceTest.java
@@ -1,4 +1,4 @@
-package tdt4140.gr1800.app.core;
+package tdt4140.gr1800.app.gpx;
 
 import java.util.Collection;
 import java.util.Iterator;
@@ -8,6 +8,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 import io.jenetics.jpx.GPX;
+import tdt4140.gr1800.app.core.GeoLocations;
 import tdt4140.gr1800.app.gpx.GpxDocumentConverter;
 import tdt4140.gr1800.app.gpx.GpxDocumentLoader;
 
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/json/GeoLocationsStreamPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/json/GeoLocationsStreamPersistenceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd72569325a6a936de4c6cba3fa16bd5a8f6253b
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/json/GeoLocationsStreamPersistenceTest.java
@@ -0,0 +1,67 @@
+package tdt4140.gr1800.app.json;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
+import tdt4140.gr1800.app.core.GeoLocationsTest;
+import tdt4140.gr1800.app.core.LatLong;
+
+public class GeoLocationsStreamPersistenceTest {
+
+	private GeoLocationsStreamPersistence persistence;
+
+	@Before
+	public void setUp() {
+		persistence = new GeoLocationsPersistence();
+	}
+
+	@Test
+	public void testLoadLocations() {
+		Collection<GeoLocations> geoLocations = null;
+		try {
+			geoLocations = persistence.loadLocations(getClass().getResourceAsStream("geoLocations.json"));
+		} catch (final Exception e) {
+			Assert.fail(e.getMessage());
+		}
+		testGeoLocationsDotJson(geoLocations);
+	}
+
+	public static Collection<GeoLocations> createGeoLocationsDotJson() {
+		return Arrays.asList(
+				new GeoLocations("1", new LatLong(63, 10), new LatLong(63.1, 10.1)),
+				new GeoLocations("2", new LatLong(64, 11), new LatLong(64.1, 11.1))
+				);
+	}
+
+	public static void testGeoLocationsDotJson(final Collection<GeoLocations> geoLocations) {
+		Assert.assertEquals(2, geoLocations.size());
+		final Iterator<GeoLocations> it = geoLocations.iterator();
+		GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(63, 10), new LatLong(63.1, 10.1));
+		GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(64, 11), new LatLong(64.1, 11.1));
+	}
+
+	@Test
+	public void testSaveLocations() {
+		final Collection<GeoLocations> geoLocations = createGeoLocationsDotJson();
+		final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+		Collection<GeoLocations> geoLocations2 = null;
+		try {
+			persistence.saveLocations(geoLocations, outputStream);
+			outputStream.close();
+			geoLocations2 = persistence.loadLocations(new ByteArrayInputStream(outputStream.toByteArray()));
+		} catch (final Exception e) {
+			e.printStackTrace();
+			Assert.fail(e.getMessage());
+		}
+		testGeoLocationsDotJson(geoLocations2);
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-route.gpx b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-route.gpx
similarity index 100%
rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-route.gpx
rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-route.gpx
diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-track.gpx b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-track.gpx
similarity index 100%
rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-track.gpx
rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-track.gpx
diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/sample1.gpx b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/sample1.gpx
similarity index 100%
rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/sample1.gpx
rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/sample1.gpx
diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/geoLocations.json b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/json/geoLocations.json
similarity index 100%
rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/geoLocations.json
rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/json/geoLocations.json
diff --git a/tdt4140-gr1800/web.server/pom.xml b/tdt4140-gr1800/web.server/pom.xml
index 57286633848e6cc761df3a89dd42d172e82c385e..785a7c64f47beff3d4cca72c84dd7191cecea7e9 100644
--- a/tdt4140-gr1800/web.server/pom.xml
+++ b/tdt4140-gr1800/web.server/pom.xml
@@ -50,6 +50,26 @@
 
 	<build>
 		<plugins>
+			<plugin>
+				<groupId>org.jacoco</groupId>
+				<artifactId>jacoco-maven-plugin</artifactId>
+				<version>0.8.0</version>
+				<executions>
+					<execution>
+						<id>pre-unit-test</id>
+						<goals>
+							<goal>prepare-agent</goal>
+						</goals>
+					</execution>
+					<execution>
+						<id>post-unit-test</id>
+						<phase>test</phase>
+						<goals>
+							<goal>report</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
 			<plugin>
 				<!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-maven-plugin -->
 				<groupId>org.eclipse.jetty</groupId>
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/DeleteMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/DeleteMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..496abed0d98b9dc8e6616da9d1492d6a611a243e
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/DeleteMethod.java
@@ -0,0 +1,23 @@
+package tdt4140.gr1800.web.server;
+
+public class DeleteMethod extends HttpMethod {
+
+	public DeleteMethod(RestEntity<?> rootEntity) {
+		super(rootEntity);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, int num) {
+		relation.deleteRelatedObject(parentEntity, num);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, String qualifier) {
+		relation.deleteRelatedObject(parentEntity, qualifier);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation) {
+		relation.deleteRelatedObject(parentEntity, getPayload());
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/EntityJsonConverter.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/EntityJsonConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfb4fd8e225d4f5ca478fca777a7b29cee80cc2a
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/EntityJsonConverter.java
@@ -0,0 +1,13 @@
+package tdt4140.gr1800.web.server;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public interface EntityJsonConverter<T> {
+	
+	public static enum JsonDepth {
+		ID, CONTENTS, CONTAINED
+	}
+	
+	public boolean supportsEntity(Object entity, JsonDepth depth);
+	public JsonNode convert2Json(T entity, JsonDepth depth);
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoLocationsGeoLocationsRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoLocationsGeoLocationsRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5474987c69502677da73f22b168e60c40b27443
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoLocationsGeoLocationsRelation.java
@@ -0,0 +1,35 @@
+package tdt4140.gr1800.web.server;
+
+import java.util.Collection;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import tdt4140.gr1800.app.core.GeoLocation;
+import tdt4140.gr1800.app.core.GeoLocations;
+
+public class GeoLocationsGeoLocationsRelation extends RestRelation<GeoLocations, GeoLocation> {
+
+	public GeoLocationsGeoLocationsRelation(String name, RestEntity<GeoLocation> relatedEntity) {
+		super(name, relatedEntity);
+	}
+
+	@Override
+	public Collection<GeoLocation> getRelated(GeoLocations entity) {
+		throw new UnsupportedOperationException("NYI");
+	}
+
+	@Override
+	public GeoLocation createRelated(GeoLocations owner, JsonNode payload) {
+		throw new UnsupportedOperationException("NYI");
+	}
+
+	@Override
+	public void updateRelated(GeoLocations owner, GeoLocation related, JsonNode payload) {
+		getDbAccess().updateGeoLocationData(owner, related);
+	}
+
+	@Override
+	public void deleteRelated(GeoLocations owner, GeoLocation related) {
+		getDbAccess().deleteGeoLocation(owner, related);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java
index 9c00904e66aaf7471f0f74c93500c946aa579f1c..0e99b506c013fae43f9f0cc222e6b19203af822c 100644
--- a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java
@@ -2,121 +2,182 @@ package tdt4140.gr1800.web.server;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.stream.Collectors;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Iterator;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import io.jenetics.jpx.Person;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import tdt4140.gr1800.app.core.GeoLocation;
 import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
-import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
+import tdt4140.gr1800.app.core.LatLong;
+import tdt4140.gr1800.app.core.Person;
+import tdt4140.gr1800.app.db.DbAccessImpl;
+import tdt4140.gr1800.app.db.IDbAccess;
+import tdt4140.gr1800.app.json.GeoLocationDeserializer;
+import tdt4140.gr1800.app.json.GeoLocationSerializer;
+import tdt4140.gr1800.app.json.GeoLocationsDeserializer;
+import tdt4140.gr1800.app.json.GeoLocationsSerializer;
+import tdt4140.gr1800.app.json.LatLongDeserializer;
+import tdt4140.gr1800.app.json.LatLongSerializer;
+import tdt4140.gr1800.app.json.PersonDeserializer;
+import tdt4140.gr1800.app.json.PersonSerializer;
 
 public class GeoServlet extends HttpServlet {
 
-	private GeoLocationsStreamPersistence persistence = new GeoLocationsJsonPersistence();
+	private IDbAccess dbAccess;
+	private RestEntity<Void> rootEntity;
+	private ObjectMapper objectMapper;
 
-	private Collection<GeoLocations> allGeoLocations = new ArrayList<GeoLocations>();
-	
 	@Override
 	public void init() throws ServletException {
-		String dataLocationsProp = System.getProperty("data.locations");
-		if (dataLocationsProp != null) {
-			String[] dataLocations = dataLocationsProp.split(",");
-			for (int i = 0; i < dataLocations.length; i++) {
-				try {
-					Collection<GeoLocations> geoLocations = persistence.loadLocations(new URL(dataLocations[i]).openStream());
-					allGeoLocations.addAll(geoLocations);
-				} catch (Exception e) {
-					System.err.println(e.getMessage());
-				}
-			}
+		String dbConnectionUrl = "jdbc:hsqldb:mem:" + getClass().getName();
+		final String dbConnectionParam = getInitParameter("dbConnectionURL");
+		if (dbConnectionParam != null) {
+			dbConnectionUrl = dbConnectionParam;
+		}
+		try {
+			dbAccess = new DbAccessImpl(dbConnectionUrl);
+		} catch (final SQLException e) {
+			throw new ServletException("Error when initializing database connection (" + dbConnectionUrl + "): " + e, e);
 		}
-		super.init();
+
+		rootEntity = new RestEntity<>();
+		final RestEntity<Person> personEntity = new RestEntity<>();
+		final RestEntity<GeoLocations> geoLocationsEntity = new RestEntity<>();
+		final RestEntity<GeoLocation> geoLocationEntity = new RestEntity<>();
+
+		final RestRelation<Void, Person> rootPersonRelation = new RootPersonsRelation("persons", personEntity);
+		rootEntity.addRelation(rootPersonRelation);
+		final RestRelation<Person, ?> personGeoLocationsRelation = new PersonGeoLocationsRelation("geoLocations", geoLocationsEntity);
+		personGeoLocationsRelation.setDbAccess(dbAccess);
+		personEntity.addRelation(personGeoLocationsRelation);
+		final RestRelation<GeoLocations, ?> geoLocationsGeoLocationsRelation = new GeoLocationsGeoLocationsRelation("geoLocations", geoLocationEntity);
+		geoLocationsGeoLocationsRelation.setDbAccess(dbAccess);
+		geoLocationsEntity.addRelation(geoLocationsGeoLocationsRelation);
+
+		objectMapper = new ObjectMapper();
+		final SimpleModule module = new SimpleModule();
+		module.addSerializer(new LatLongSerializer());
+		module.addSerializer(new GeoLocationSerializer());
+		module.addSerializer(new GeoLocationsSerializer());
+		final PersonSerializer personSerializer = new PersonSerializer();
+		personSerializer.setIdProvider(dbAccess.getPersonIdProvider());
+		module.addSerializer(personSerializer);
+		module.addDeserializer(LatLong.class, new LatLongDeserializer());
+		module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer());
+		module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer());
+		module.addDeserializer(Person.class, new PersonDeserializer());
+		objectMapper.registerModule(module);
 	}
-	
+
 	// REST URL structure, according to https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/
 	// persons/<id>/geoLocations/<num>/geoLocations/<num>
 
 	// GET variants
-	// persons: Get all Person objects. Do we allow that? Should we return a list of <id> values or all the person entities (with some subset of properties) 
+	// persons: Get all Person objects. Do we allow that? Should we return a list of <id> values or all the person entities (with some subset of properties)
 	// persons/<id>: Get a specific Person object
 	// persons/name or email: Get a specific Person object, with the provided name or email (with a '@')
 	// persons/<id>/geoLocations: Get all the GeoLocations objects, with (some subset of) properties
 	// persons/<id>/geoLocations/<num>: Get a specific GeoLocations object
 	// persons/<id>/geoLocations/<num>/geoLocations: Get all GeoLocation objects, with (some subset of) properties
 	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Get a specific GeoLocations object
-	
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    		String path = request.getPathInfo();
-    		Collection<GeoLocations> geoLocations = null;
-    		if (path != null) {
-    			if (path.startsWith("/")) {
-    				path = path.substring(1);
-    			}
-    			String[] segments = path.split("\\/");
-    			if (segments.length == 1) {
-    				geoLocations = allGeoLocations.stream().filter(geoLocation -> segments[0].equals(geoLocation.getName())).collect(Collectors.toList());
-    			}
-    		} else {
-    			geoLocations = allGeoLocations;
-    		}
-    		if (geoLocations == null) {
+
+	@Override
+	protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+		final HttpMethod method = new GetMethod(rootEntity);
+		final Object result = method.doMethod(getPathSegments(request));
+		if (result == null) {
 			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-    		} else {
-    			response.setContentType("application/json");
-    			response.setStatus(HttpServletResponse.SC_OK);
-    			try (OutputStream output = response.getOutputStream()) {
-				persistence.saveLocations(geoLocations, output);
-			} catch (Exception e) {
-				response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-			}
-    		}
-    }
+		} else {
+			respondWithJson(response, result);
+		}
+	}
+
+	protected void respondWithJson(final HttpServletResponse response, final Object result) {
+		response.setContentType("application/json");
+		response.setStatus(HttpServletResponse.SC_OK);
+		try (OutputStream output = response.getOutputStream()) {
+			objectMapper.writeValue(output, result);
+		} catch (final Exception e) {
+			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	protected Iterator<String> getPathSegments(final HttpServletRequest request) throws ServletException {
+		final String path = request.getPathInfo();
+		if (path == null) {
+			throw new ServletException("Path cannot be empty");
+		}
+		final Iterator<String> segments = Arrays.asList(path.split("\\/")).iterator();
+		return segments;
+	}
 
 	// POST variants
 	// persons: Create a new Person object, with properties in the payload
 	// persons/<id>: Not allowed
-    // persons/<id>/geoLocations: Create a new GeoLocations object, with properties in the payload
+	// persons/<id>/geoLocations: Create a new GeoLocations object, with properties in the payload
 	// persons/<id>/geoLocations/<num>: Not allowed
 	// persons/<id>/geoLocations/<num>/geoLocations: Create a new GeoLocation object, with properties in the payload
 	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Not allowed
 
-    @Override
-    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-    		super.doPost(req, resp);
-    }
-    
+	@Override
+	protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+		final HttpMethod method = new PostMethod(rootEntity);
+		final JsonNode jsonNode = objectMapper.readTree(request.getInputStream());
+		method.setPayload(jsonNode);
+		final Object result = method.doMethod(getPathSegments(request));
+		if (result == null) {
+			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+		} else {
+			respondWithJson(response, result);
+		}
+	}
+
 	// PUT variants
 	// persons: Not allowed
 	// persons/<id>: Update specific Person object
-    // persons/<id>/geoLocations: Not allowed
+	// persons/<id>/geoLocations: Not allowed
 	// persons/<id>/geoLocations/<num>: Update specific GeoLocations object
 	// persons/<id>/geoLocations/<num>/geoLocations: Not allowed
 	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Update specific GeoLocations object
 
-    @Override
-    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-    		// TODO Auto-generated method stub
-    		super.doPut(req, resp);
-    }
-
-    // DELETE variants
-    // persons: Not allowed
-    // persons/<id>: Delete specific Person object
-    // persons/<id>/geoLocations: Delete all GeoLocations objects?
-    // persons/<id>/geoLocations/<num>: Delete specific GeoLocations object
-    // persons/<id>/geoLocations/<num>/geoLocations: Delete all GeoLocation objects?
-    // persons/<id>/geoLocations/<num>/geoLocations/<num>: Delete specific GeoLocation object
-
-    @Override
-    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-    		// TODO Auto-generated method stub
-    		super.doDelete(req, resp);
-    }
+	@Override
+	protected void doPut(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+		final HttpMethod method = new PutMethod(rootEntity);
+		final JsonNode jsonNode = objectMapper.readTree(request.getInputStream());
+		method.setPayload(jsonNode);
+		final Object result = method.doMethod(getPathSegments(request));
+		if (result == null) {
+			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+		} else {
+			respondWithJson(response, result);
+		}
+	}
+
+	// DELETE variants
+	// persons: Not allowed
+	// persons/<id>: Delete specific Person object
+	// persons/<id>/geoLocations: Delete all GeoLocations objects?
+	// persons/<id>/geoLocations/<num>: Delete specific GeoLocations object
+	// persons/<id>/geoLocations/<num>/geoLocations: Delete all GeoLocation objects?
+	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Delete specific GeoLocation object
+
+	@Override
+	protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+		final HttpMethod method = new DeleteMethod(rootEntity);
+		final Object result = method.doMethod(getPathSegments(request));
+		if (result == null) {
+			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+		} else {
+			respondWithJson(response, result);
+		}
+	}
 }
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GetMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GetMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a0ebead3fb99093c000bcf2006bb57e5a7a301c
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GetMethod.java
@@ -0,0 +1,23 @@
+package tdt4140.gr1800.web.server;
+
+public class GetMethod extends HttpMethod {
+
+	public GetMethod(RestEntity<?> rootEntity) {
+		super(rootEntity);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, int num) {
+		doRelated(relation, num);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, String qualifier) {
+		doRelated(relation, qualifier);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation) {
+		doRelated(relation);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/HttpMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/HttpMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b34ad2b6e2797e254b317b0f2f0cbc452f5567d
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/HttpMethod.java
@@ -0,0 +1,161 @@
+package tdt4140.gr1800.web.server;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public abstract class HttpMethod {
+
+	private final RestEntity<?> rootEntity;
+
+	public HttpMethod(final RestEntity<?> rootEntity) {
+		this.rootEntity = rootEntity;
+	}
+
+	protected Object parentEntity = null, entity = null;
+	protected Collection<?> entities = null;
+
+	private Map<String, Object> params;
+	private JsonNode payload;
+
+	public Map<String, Object> getParameters() {
+		return params;
+	}
+
+	public void setParameters(final Map<String, ? extends Object> params) {
+		this.params = new HashMap<String, Object>(params);
+	}
+
+	public void setParameters(final Object... params) {
+		this.params = new HashMap<String, Object>();
+		for (int i = 0; i < params.length; i += 2) {
+			this.params.put(String.valueOf(params[i]), params[i + 1]);
+		}
+	}
+
+	protected Object getParameter(final String param) {
+		if (params.containsKey(param)) {
+			return params.get(param);
+		}
+		if (payload instanceof ObjectNode) {
+			return ((ObjectNode) payload).get(param);
+		}
+		return null;
+	}
+
+	private final JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(false);
+
+	public void setPayload(final JsonNode payload) {
+		this.payload = payload;
+	}
+
+	public void setPayload(final Object... params) {
+		final ObjectNode objectNode = jsonNodeFactory.objectNode();
+		for (int i = 0; i < params.length; i += 2) {
+			setPayloadValue(objectNode, String.valueOf(params[i]), params[i + 1]);
+		}
+		this.payload = objectNode;
+	}
+
+	public void setPayload(final Map<String, ? extends Object> params) {
+		final ObjectNode objectNode = jsonNodeFactory.objectNode();
+		for (final Map.Entry<String, ? extends Object> entry : params.entrySet()) {
+			setPayloadValue(objectNode, entry.getKey(), entry.getValue());
+		}
+		this.payload = objectNode;
+	}
+
+	protected void setPayloadValue(final ObjectNode objectNode, final String key, final Object value) {
+		if (value instanceof String) {
+			objectNode.put(key, (String) value);
+		} else if (value instanceof Number) {
+			objectNode.put(key, ((Number) value).doubleValue());
+		} else if (value instanceof Boolean) {
+			objectNode.put(key, (Boolean) value);
+		}
+	}
+
+	public JsonNode getPayload() {
+		return payload;
+	}
+
+	public Object doMethod(final String... segments) {
+		return doMethod(Arrays.asList(segments));
+	}
+
+	public Object doMethod(final Iterable<String> segments) {
+		return doMethod(segments.iterator());
+	}
+
+	public Object doMethod(final Iterator<String> segments) {
+		RestEntity<?> restEntity = rootEntity;
+		entity = null;
+		while (segments.hasNext()) {
+			final String segment = segments.next();
+			if (segment.length() == 0) {
+				continue;
+			}
+			final RestRelation<?, ?> relation = restEntity.getRelation(segment);
+			parentEntity = entity;
+			entity = null;
+			entities = null;
+			if (segments.hasNext()) {
+				final String qualifier = segments.next();
+				try {
+					final int num = Integer.valueOf(qualifier);
+					if (segments.hasNext()) {
+						doRelated(relation, num);
+					} else {
+						doMethod(relation, num);
+					}
+				} catch (final NumberFormatException e) {
+					if (segments.hasNext()) {
+						doRelated(relation, qualifier);
+					} else {
+						doMethod(relation, qualifier);
+					}
+				}
+				restEntity = relation.getRelatedEntity();
+			} else {
+				if (segments.hasNext()) {
+					doRelated(relation);
+				} else {
+					doMethod(relation);
+				}
+			}
+			if (entity == null) {
+				break;
+			}
+		}
+		if (entities != null) {
+			return entities;
+		}
+		return entity;
+	}
+
+	protected abstract void doMethod(RestRelation<?, ?> relation, int num);
+
+	protected abstract void doMethod(RestRelation<?, ?> relation, String qualifier);
+
+	protected abstract void doMethod(RestRelation<?, ?> relation);
+
+	//
+
+	protected void doRelated(final RestRelation<?, ?> relation) {
+		entities = relation.getRelatedObjects(parentEntity);
+	}
+
+	protected void doRelated(final RestRelation<?, ?> relation, final String qualifier) {
+		entity = relation.getRelatedObject(parentEntity, qualifier);
+	}
+
+	protected void doRelated(final RestRelation<?, ?> relation, final int num) {
+		entity = relation.getRelatedObject(parentEntity, num);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PersonGeoLocationsRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PersonGeoLocationsRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..f60eea9ca12ae218a3bbbd27a6f5862ef7847c0d
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PersonGeoLocationsRelation.java
@@ -0,0 +1,97 @@
+package tdt4140.gr1800.web.server;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.Person;
+
+public class PersonGeoLocationsRelation extends RestRelation<Person, GeoLocations> {
+
+	public PersonGeoLocationsRelation(String name, RestEntity<GeoLocations> relatedEntity) {
+		super(name, relatedEntity);
+	}
+
+	@Override
+	public Collection<GeoLocations> getRelated(Person entity) {
+		return getDbAccess().getGeoLocations(entity, false);
+	}
+
+	@Override
+	public GeoLocations createRelated(Person owner, JsonNode payload) {
+		GeoLocations geoLocations = getDbAccess().createGeoLocations(owner);
+		updateRelated(owner, geoLocations, payload);
+		return geoLocations;
+	}
+	
+	private final static Collection<String> NULL_STRINGS = Arrays.asList("", "\0");
+	
+	protected boolean isNull(String s) {
+		return NULL_STRINGS.contains(s);
+	}
+	
+	protected <T, V> void set(T t, ObjectNode jsonNode, String fieldName, Consumer<String> setter) {
+		if (jsonNode.has(fieldName)) {
+			String s = jsonNode.get(fieldName).asText();
+			setter.accept(isNull(s) ? null : s);
+		}
+	}
+	protected <T, V> void set(T t, ObjectNode jsonNode, String fieldName, Consumer<V> setter, Function<String, V> parser, Class<? extends Exception> exClass) {
+		if (jsonNode.has(fieldName)) {
+			String s = jsonNode.get(fieldName).asText();
+			try {
+				setter.accept(isNull(s) ? null : parser.apply(s));
+			} catch (RuntimeException e) {
+				if (exClass != null && (! exClass.isAssignableFrom(e.getClass()))) {
+					throw e;
+				}
+			}
+		}
+	}
+	
+	@Override
+	public void updateRelated(Person owner, GeoLocations related, JsonNode payload) {
+		if (payload instanceof ObjectNode) {
+			ObjectNode objectNode = (ObjectNode) payload;
+			set(related, objectNode, "name", related::setName);
+			set(related, objectNode, "description", related::setDescription);
+			set(related, objectNode, "date", related::setDate, LocalDate::parse, DateTimeParseException.class);
+			set(related, objectNode, "time", related::setTime, LocalTime::parse, DateTimeParseException.class);
+			set(related, objectNode, "zone", related::setZone, ZoneId::of, DateTimeException.class);
+			if (objectNode.has("tags")) {
+				JsonNode tagsNode = objectNode.get("tags");
+				String[] tags = null;
+				if (tagsNode instanceof ArrayNode) {
+					ArrayNode tagsArray = (ArrayNode) tagsNode;
+					tags = new String[tagsArray.size()];
+					for (int i = 0; i < tags.length; i++) {
+						tags[i] = tagsArray.get(i).asText();
+					}
+				} else if (tagsNode instanceof TextNode) {
+					tags = ((TextNode) tagsNode).asText().split("[ ,;]");
+				}
+				if (tags != null) {
+					related.setTags(tags);
+				}
+			}
+		}
+		getDbAccess().updateGeoLocationsData(related);
+	}
+
+	@Override
+	public void deleteRelated(Person owner, GeoLocations related) {
+		getDbAccess().deleteGeoLocations(related);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PostMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PostMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c6558487922c129674be4238a3be9b7051a60c6
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PostMethod.java
@@ -0,0 +1,23 @@
+package tdt4140.gr1800.web.server;
+
+public class PostMethod extends HttpMethod {
+
+	public PostMethod(RestEntity<?> rootEntity) {
+		super(rootEntity);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, int num) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, String qualifier) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation) {
+		entity = relation.createRelatedObject(parentEntity, getPayload());
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PutMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PutMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..37198d0d537e079a1f151ab436491ba671574b39
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PutMethod.java
@@ -0,0 +1,25 @@
+package tdt4140.gr1800.web.server;
+
+public class PutMethod extends HttpMethod {
+
+	public PutMethod(RestEntity<?> rootEntity) {
+		super(rootEntity);
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, int num) {
+		doRelated(relation, num);
+		relation.updateRelatedObject(parentEntity, entity, getPayload());
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation, String qualifier) {
+		doRelated(relation, qualifier);
+		relation.updateRelatedObject(parentEntity, entity, getPayload());
+	}
+
+	@Override
+	protected void doMethod(RestRelation<?, ?> relation) {
+		throw new UnsupportedOperationException();
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestEntity.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..984a4609d4469bf3fed6714495d683396e4e95cf
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestEntity.java
@@ -0,0 +1,22 @@
+package tdt4140.gr1800.web.server;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class RestEntity<T> {
+
+	private final Collection<RestRelation<T, ?>> relations = new ArrayList<RestRelation<T,?>>();
+	
+	public RestRelation<T, ?> getRelation(String relationName) {
+		for (RestRelation<T, ?> relation : relations) {
+			if (relationName.equals(relation.getName())) {
+				return relation;
+			}
+		}
+		return null;
+	}
+	
+	public void addRelation(RestRelation<T, ?> relation) {
+		this.relations.add(relation);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8f290360dd66f85bd18ddd000a6db1aa7ae4558
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestRelation.java
@@ -0,0 +1,142 @@
+package tdt4140.gr1800.web.server;
+
+import java.util.Collection;
+import java.util.NoSuchElementException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import tdt4140.gr1800.app.db.IDbAccess;
+
+public abstract class RestRelation<OE, RE> {
+
+	private final String name;
+
+	private RestEntity<RE> relatedEntity;
+
+	protected RestRelation(String name, RestEntity<RE> relatedEntity) {
+		super();
+		this.name = name;
+		this.relatedEntity = relatedEntity;
+	}
+	protected RestRelation(String name) {
+		this(name, null);
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public RestEntity<RE> getRelatedEntity() {
+		return relatedEntity;
+	}
+
+	private IDbAccess dbAccess;
+	
+	public IDbAccess getDbAccess() {
+		return dbAccess;
+	}
+	
+	public void setDbAccess(IDbAccess dbAccess) {
+		this.dbAccess = dbAccess;
+	}
+	
+	public void setRelatedEntity(RestEntity<RE> relatedEntity) {
+		this.relatedEntity = relatedEntity;
+	}
+
+	// GET method: /name
+	protected abstract Collection<RE> getRelated(OE entity);
+	
+	protected Collection<RE> getRelatedObjects(Object entity) {
+		return getRelated((OE) entity);
+	}
+
+	// GET method: /name/<num>
+	protected RE getRelated(OE entity, int num) {
+		Collection<RE> col = getRelated(entity);
+		if (num < 0) {
+			num = col.size() + num;
+		}
+		for (RE related : col) {
+			if (num == 0) {
+				return related;
+			}
+			num--;
+		}
+		return null;
+	}
+	public RE getRelatedObject(Object entity, int num) {
+		return getRelated((OE) entity, num);
+	}
+	
+	protected boolean isRelated(RE related, String qualifier) {
+		return false;
+	}
+
+	// GET method: /name/<qualifier>
+	protected RE getRelated(OE entity, String qualifier) {
+		for (RE related : getRelated(entity)) {
+			if (isRelated(related, qualifier)) {
+				return related;
+			}
+		}
+		return null;
+	}
+	public RE getRelatedObject(Object entity, String qualifier) {
+		return getRelated((OE) entity, qualifier);
+	}
+
+	// POST method
+	protected abstract RE createRelated(OE owner, JsonNode payload);
+
+	public Object createRelatedObject(Object owner, JsonNode payload) {
+		return createRelated((OE) owner, payload);
+	}
+	
+	// PUT method
+	protected abstract void updateRelated(OE owner, RE related, JsonNode payload);
+
+	public void updateRelatedObject(Object owner, Object related, JsonNode payload) {
+		updateRelated((OE) owner, (RE) related, payload);
+	}
+	
+	// DELETE method
+	protected abstract void deleteRelated(OE owner, RE related);
+	
+	public void deleteRelatedObject(Object owner, Object related) {
+		deleteRelated((OE) owner, (RE) related);
+	}
+
+	// DELETE method: /name/<num>
+	protected void deleteRelated(OE owner, int num) {
+		Collection<RE> col = getRelated(owner);
+		int count = (num < 0 ? col.size() + num : num);
+		for (RE related : col) {
+			if (count == 0) {
+				deleteRelated(owner, related);
+				return;
+			}
+			count--;
+		}
+		throw new NoSuchElementException("No element with '" + num + "' qualifier");
+	}
+
+	public void deleteRelatedObject(Object owner, int num) {
+		deleteRelated((OE) owner, num);
+	}
+	
+	// DELETE method: /name/<qualifier>
+	protected void deleteRelated(OE owner, String qualifier) {
+		for (RE related : getRelated(owner)) {
+			if (isRelated(related, qualifier)) {
+				deleteRelated(owner, related);
+				return;
+			}
+		}
+		throw new NoSuchElementException("No element with '" + qualifier + "' qualifier");
+	}
+	
+	public void deleteRelatedObject(Object owner, String qualifier) {
+		deleteRelated((OE) owner, qualifier);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RootPersonsRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RootPersonsRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..f4e3aba7d2ea01a82d5eb71030f9cf0b7511eb79
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RootPersonsRelation.java
@@ -0,0 +1,64 @@
+package tdt4140.gr1800.web.server;
+
+import java.util.Collection;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.Person;
+
+public class RootPersonsRelation extends RestRelation<Void, Person> {
+
+	public RootPersonsRelation(String name, RestEntity<Person> relatedEntity) {
+		super(name, relatedEntity);
+	}
+
+	@Override
+	public Collection<Person> getRelated(Void entity) {
+		return getDbAccess().getAllPersons(false);
+	}
+
+	@Override
+	protected Person getRelated(Void entity, int num) {
+		return getDbAccess().getPerson(num, false);
+	}
+	
+	@Override
+	protected boolean isRelated(Person related, String qualifier) {
+		String propertyValue = qualifier.contains("@") ? related.getEmail() : related.getName();
+		return qualifier.equals(propertyValue);
+	}
+	
+	@Override
+	public Person createRelated(Void owner, JsonNode payload) {
+		String name = null, email = null;
+		if (payload instanceof ObjectNode) {
+			ObjectNode objectNode = (ObjectNode) payload;
+			name = objectNode.get("name").asText();
+			email = objectNode.get("email").asText();
+		}
+		if (name == null || email == null) {
+			throw new IllegalArgumentException("name and email must be set: " + payload);
+		}
+		return getDbAccess().createPerson(name, email);
+	}
+
+	@Override
+	public void updateRelated(Void owner, Person related, JsonNode payload) {
+		if (payload instanceof ObjectNode) {
+			ObjectNode objectNode = (ObjectNode) payload;
+			if (objectNode.has("name")) {
+				related.setName(objectNode.get("name").asText());
+			}
+			if (objectNode.has("email")) {
+				related.setEmail(objectNode.get("email").asText());
+			}
+		}
+		getDbAccess().updatePersonData(related);
+	}
+
+	@Override
+	public void deleteRelated(Void owner, Person related) {
+		getDbAccess().deletePerson(related);
+	}
+}
diff --git a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java
index a230c446ad7269721f7913f58b793a31851f129c..1b2fd7c292ac7ba7ea971f8e64d7af33e2e97efa 100644
--- a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java
+++ b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java
@@ -1,57 +1,79 @@
 package tdt4140.gr1800.web.server;
 
+import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
 import java.net.URL;
-import java.util.Collection;
-import java.util.Iterator;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
-import tdt4140.gr1800.app.core.GeoLocated;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import tdt4140.gr1800.app.core.GeoLocation;
 import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
 import tdt4140.gr1800.app.core.LatLong;
-import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
+import tdt4140.gr1800.app.core.Person;
+import tdt4140.gr1800.app.json.GeoLocationDeserializer;
+import tdt4140.gr1800.app.json.GeoLocationSerializer;
+import tdt4140.gr1800.app.json.GeoLocationsDeserializer;
+import tdt4140.gr1800.app.json.GeoLocationsSerializer;
+import tdt4140.gr1800.app.json.LatLongDeserializer;
+import tdt4140.gr1800.app.json.LatLongSerializer;
+import tdt4140.gr1800.app.json.PersonDeserializer;
+import tdt4140.gr1800.app.json.PersonSerializer;
 
 public class GeoLocationsServerIT {
 
-	private GeoLocationsStreamPersistence persistence = new GeoLocationsJsonPersistence();
-	
-	private Collection<GeoLocations> get(String path, int size) throws Exception {
-		URL url = new URL("http://localhost:8080/geo" + path);
-		try (InputStream inputStream = url.openStream()) {
-			Collection<GeoLocations> geoLocations = persistence.loadLocations(inputStream);
-			Assert.assertEquals(size, geoLocations.size());
-			return geoLocations;
-		}
-	}
+	private ObjectMapper objectMapper;
 
-	private LatLong[][] latLongs = {
-		{ new LatLong(63,  10), new LatLong(63.1,  10.1)},
-		{ new LatLong(64,  11), new LatLong(64.1,  11.1)}
-	};
-	
-	@Test
-	public void testGet() throws Exception {
-		Collection<GeoLocations> geoLocations = get("", 2);
-		Iterator<GeoLocations> it = geoLocations.iterator();
-		checkGeoLocations(it.next(), "1", latLongs[0]);
-		checkGeoLocations(it.next(), "2", latLongs[1]);
-
-		Collection<GeoLocations> geoLocations1 = get("/1", 1);
-		checkGeoLocations(geoLocations1.iterator().next(), "1", latLongs[0]);
-		Collection<GeoLocations> geoLocations2 = get("/2", 1);
-		checkGeoLocations(geoLocations2.iterator().next(), "2", latLongs[1]);
+	@Before
+	public void setUp() {
+		objectMapper = new ObjectMapper();
+		final SimpleModule module = new SimpleModule();
+		module.addSerializer(new LatLongSerializer());
+		module.addSerializer(new GeoLocationSerializer());
+		module.addSerializer(new GeoLocationsSerializer());
+		module.addSerializer(new PersonSerializer());
+		module.addDeserializer(LatLong.class, new LatLongDeserializer());
+		module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer());
+		module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer());
+		module.addDeserializer(Person.class, new PersonDeserializer());
+		objectMapper.registerModule(module);
 	}
 
-	private void checkGeoLocations(GeoLocations geoLocations, String name, LatLong... latLongs) {
-		Assert.assertEquals(name, geoLocations.getName());
-		Iterator<GeoLocated> it = geoLocations.iterator();
-		for (int i = 0; i < latLongs.length; i++) {
-			Assert.assertTrue(it.hasNext());
-			latLongs[i].equalsLatLong(it.next());
+	@Test
+	public void testPostPerson() throws Exception {
+		final Person person = new Person();
+		person.setName("Hallvard");
+		person.setEmail("hal@ntnu.no");
+		final URL url = new URL("http://localhost:8080/geo/persons");
+		final HttpURLConnection con = ((HttpURLConnection) url.openConnection());
+		con.setRequestMethod("POST");
+		con.setDoOutput(true);
+		con.setDoInput(true);
+		final ByteArrayOutputStream out = new ByteArrayOutputStream();
+		objectMapper.writeValue(out, person);
+		out.close();
+		final byte[] bytes = out.toByteArray();
+		con.setFixedLengthStreamingMode(bytes.length);
+		con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+		con.connect();
+		try (OutputStream conOut = con.getOutputStream()) {
+			conOut.write(bytes);
+		}
+		try (InputStream conIn = url.openStream()) {
+			final JsonNode jsonTree = objectMapper.readTree(conIn);
+			Assert.assertTrue(jsonTree instanceof ObjectNode);
+			Assert.assertTrue(((ObjectNode) jsonTree).has("id"));
+			final Person conPerson = objectMapper.treeToValue(jsonTree, Person.class);
+			Assert.assertEquals(person.getName(), conPerson.getName());
+			Assert.assertEquals(person.getEmail(), conPerson.getEmail());
 		}
-		Assert.assertFalse(it.hasNext());
 	}
 }
diff --git a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/HttpMethodTest.java b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/HttpMethodTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bf4d06618f5748bc7a1a6395d6cf068e564cc0a
--- /dev/null
+++ b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/HttpMethodTest.java
@@ -0,0 +1,301 @@
+package tdt4140.gr1800.web.server;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import tdt4140.gr1800.app.core.GeoLocation;
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.LatLong;
+import tdt4140.gr1800.app.core.Person;
+import tdt4140.gr1800.app.db.AbstractDbAccessImpl;
+import tdt4140.gr1800.app.db.IDbAccess;
+import tdt4140.gr1800.app.json.GeoLocationDeserializer;
+import tdt4140.gr1800.app.json.GeoLocationSerializer;
+import tdt4140.gr1800.app.json.GeoLocationsDeserializer;
+import tdt4140.gr1800.app.json.GeoLocationsSerializer;
+import tdt4140.gr1800.app.json.LatLongDeserializer;
+import tdt4140.gr1800.app.json.LatLongSerializer;
+import tdt4140.gr1800.app.json.PersonDeserializer;
+import tdt4140.gr1800.app.json.PersonSerializer;
+
+public class HttpMethodTest {
+
+	private IDbAccess dbAccess;
+	private RestEntity<Void> rootEntity;
+
+	private Map<String, Object> dbAccessRecord;
+	private ObjectMapper objectMapper;
+
+	@Before
+	public void setUpEntities() {
+
+		objectMapper = new ObjectMapper();
+		final SimpleModule module = new SimpleModule();
+		module.addSerializer(new LatLongSerializer());
+		module.addSerializer(new GeoLocationSerializer());
+		module.addSerializer(new GeoLocationsSerializer());
+		module.addSerializer(new PersonSerializer());
+		module.addDeserializer(LatLong.class, new LatLongDeserializer());
+		module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer());
+		module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer());
+		module.addDeserializer(Person.class, new PersonDeserializer());
+		objectMapper.registerModule(module);
+
+		dbAccessRecord = new HashMap<>();
+		dbAccess = new AbstractDbAccessImpl() {
+
+			@Override
+			public Person createPerson(final String name, final String email) {
+				final Person person = super.createPerson(name, email);
+				personIds.set(person, personIds.size() + 1);
+				dbAccessRecord.put("createPerson", "name=" + name + ";email=" + email);
+				return person;
+			}
+
+			@Override
+			public Collection<Person> getAllPersons(final boolean refresh) {
+				final Collection<Person> persons = super.getAllPersons(refresh);
+				dbAccessRecord.put("getAllPersons", refresh);
+				return persons;
+			}
+
+			@Override
+			public Person getPerson(final int id, final boolean refresh) {
+				final Person person = super.getPerson(id, refresh);
+				dbAccessRecord.put("getPerson", id);
+				return person;
+			}
+
+			@Override
+			public void updatePersonData(final Person person) {
+				dbAccessRecord.put("updatePersonData", "name=" + person.getName() + ";email=" + person.getEmail());
+			}
+
+			@Override
+			public void deletePerson(final Person person) {
+				super.deletePerson(person);
+				dbAccessRecord.put("deletePerson", person.getName());
+			}
+
+			//
+
+			@Override
+			public GeoLocations createGeoLocations(final Person owner) {
+				final GeoLocations geoLocations = super.createGeoLocations(owner);
+				geoLocationsIds.set(geoLocations, geoLocationsIds.size() + 1);
+				dbAccessRecord.put("createGeoLocations", owner.getGeoLocations((String[]) null).size());
+				return geoLocations;
+			}
+
+			@Override
+			public Collection<GeoLocations> getGeoLocations(final Person owner, final boolean refresh) {
+				final Collection<GeoLocations> geoLocations = super.getGeoLocations(owner, refresh);
+				dbAccessRecord.put("getGeoLocations", refresh);
+				return geoLocations;
+			}
+
+			@Override
+			public void updateGeoLocationsData(final GeoLocations geoLocations) {
+				dbAccessRecord.put("updateGeoLocationsData", geoLocations.getName());
+			}
+
+			@Override
+			public void deleteGeoLocations(final GeoLocations geoLocations) {
+				super.deleteGeoLocations(geoLocations);
+				dbAccessRecord.put("deleteGeoLocations", geoLocations.getName());
+			}
+
+			@Override
+			public void updateGeoLocationData(final GeoLocations geoLocations, final GeoLocation geoLocation) {
+			}
+		};
+
+		rootEntity = new RestEntity<>();
+		final RestEntity<Person> personEntity = new RestEntity<>();
+		final RestEntity<GeoLocations> geoLocationsEntity = new RestEntity<>();
+		final RestEntity<GeoLocation> geoLocationEntity = new RestEntity<>();
+
+		final RestRelation<Void, Person> rootPersonRelation = new RootPersonsRelation("persons", personEntity);
+		rootEntity.addRelation(rootPersonRelation);
+		rootPersonRelation.setDbAccess(dbAccess);
+
+		final RestRelation<Person, ?> personGeoLocationsRelation = new PersonGeoLocationsRelation("geoLocations", geoLocationsEntity);
+		personGeoLocationsRelation.setDbAccess(dbAccess);
+		personEntity.addRelation(personGeoLocationsRelation);
+
+		final RestRelation<GeoLocations, ?> geoLocationsGeoLocationsRelation = new GeoLocationsGeoLocationsRelation("geoLocations", geoLocationEntity);
+		geoLocationsGeoLocationsRelation.setDbAccess(dbAccess);
+		geoLocationsEntity.addRelation(geoLocationsGeoLocationsRelation);
+	}
+
+	@Test
+	// create Person
+	public void testPostPerson() {
+		final HttpMethod method = new PostMethod(rootEntity);
+		method.setPayload("name" , "Hallvard", "email" , "hal@ntnu.no");
+		final Object result = method.doMethod("persons");
+		Assert.assertEquals("name=Hallvard;email=hal@ntnu.no", dbAccessRecord.get("createPerson"));
+		Assert.assertTrue(result instanceof Person);
+	}
+
+	@Test
+	// read Person
+	public void testGetPerson() {
+		final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final HttpMethod method = new GetMethod(rootEntity);
+		final Object result = method.doMethod("persons");
+		Assert.assertEquals(false, dbAccessRecord.get("getAllPersons"));
+		Assert.assertTrue(result instanceof Collection);
+		final Collection<?> persons = (Collection<?>) result;
+		Assert.assertEquals(1, persons.size());
+		Assert.assertEquals(person, persons.iterator().next());
+	}
+
+	@Test
+	// update Person
+	public void testPutPerson() {
+		final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final HttpMethod method = new PutMethod(rootEntity);
+		final int id = 1;
+		method.setPayload("name" , "Hallvard Trætteberg", "email" , "hallvard.traetteberg@gmail.com");
+		final Object result = method.doMethod("persons", String.valueOf(id));
+		Assert.assertEquals("name=Hallvard Trætteberg;email=hallvard.traetteberg@gmail.com", dbAccessRecord.get("updatePersonData"));
+		Assert.assertTrue(result instanceof Person);
+		Assert.assertEquals("Hallvard Trætteberg", person.getName());
+		Assert.assertEquals("hallvard.traetteberg@gmail.com", person.getEmail());
+	}
+
+	@Test
+	// delete Person
+	public void testDeletePerson() {
+		dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final HttpMethod method = new DeleteMethod(rootEntity);
+		method.doMethod("persons", "Hallvard");
+		Assert.assertEquals("Hallvard", dbAccessRecord.get("deletePerson"));
+		Assert.assertTrue(dbAccess.getAllPersons(false).isEmpty());
+	}
+
+	@Test
+	// create GeoLocations
+	public void testPostGeoLocations() {
+		dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final HttpMethod method = new PostMethod(rootEntity);
+		method.setPayload(
+				"name" , "geoLocs1",
+				"description" , "my first geo-location",
+				"date", LocalDate.of(2018, 3, 14).toString(),
+				"time", LocalTime.of(11, 14).toString(),
+				"zone", "Europe/Oslo",
+				"tags", "tag1,tag2"
+				);
+		final Object result = method.doMethod("persons", "Hallvard", "geoLocations");
+		Assert.assertEquals(1, dbAccessRecord.get("createGeoLocations"));
+		Assert.assertTrue(result instanceof GeoLocations);
+		final GeoLocations geoLocations = (GeoLocations) result;
+		Assert.assertEquals("geoLocs1", geoLocations.getName());
+		Assert.assertEquals("my first geo-location", geoLocations.getDescription());
+		Assert.assertEquals(LocalDate.of(2018, 3, 14), geoLocations.getDate());
+		Assert.assertEquals(LocalTime.of(11, 14), geoLocations.getTime());
+		Assert.assertEquals(ZoneId.of("Europe/Oslo"), geoLocations.getZone());
+		Assert.assertEquals("tag1,tag2", geoLocations.getTags(null, ",", null));
+	}
+
+	@Test
+	// read GeoLocations
+	public void testGetGeoLocations() {
+		final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		dbAccess.createGeoLocations(person);
+		dbAccess.createGeoLocations(person);
+		final HttpMethod method = new GetMethod(rootEntity);
+		final Object result = method.doMethod("persons", "Hallvard", "geoLocations");
+		Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations"));
+		Assert.assertTrue(result instanceof Collection<?>);
+		Assert.assertEquals(2, ((Collection<?>) result).size());
+
+		try {
+			objectMapper.writerWithDefaultPrettyPrinter().writeValue(System.out, person);
+		} catch (final Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	@Test
+	// read GeoLocations
+	public void testGetGeoLocations1() {
+		final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final GeoLocations geoLocations1 = dbAccess.createGeoLocations(person);
+		final GeoLocations geoLocations2 = dbAccess.createGeoLocations(person);
+		final HttpMethod method = new GetMethod(rootEntity);
+		final Object result0 = method.doMethod("persons", "Hallvard", "geoLocations", "0");
+		Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations"));
+		Assert.assertSame(geoLocations1, result0);
+		final Object result1 = method.doMethod("persons", "Hallvard", "geoLocations", "1");
+		Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations"));
+		Assert.assertSame(geoLocations2, result1);
+		final Object result2m = method.doMethod("persons", "Hallvard", "geoLocations", "-2");
+		Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations"));
+		Assert.assertSame(geoLocations1, result2m);
+		final Object result1m = method.doMethod("persons", "Hallvard", "geoLocations", "-1");
+		Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations"));
+		Assert.assertSame(geoLocations2, result1m);
+	}
+
+	@Test
+	// update GeoLocations
+	public void testUpdateGeoLocations() {
+		final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final GeoLocations geoLocations1 = dbAccess.createGeoLocations(person);
+		geoLocations1.setName("geoLocs1");
+		geoLocations1.setDescription("my first geo-location");
+		geoLocations1.setDate(LocalDate.of(2018, 3, 14));
+		geoLocations1.setTime(LocalTime.of(11, 14));
+		geoLocations1.setZone("Europe/Oslo");
+		geoLocations1.setTags("tag1");
+		final HttpMethod method = new PutMethod(rootEntity);
+		method.setPayload(
+				"name" , "geoLocs2",
+				"description" , "my second geo-location",
+				"date", LocalDate.of(2018, 3, 15).toString(),
+				"time", LocalTime.of(11, 15).toString(),
+				"zone", "Europe/Paris",
+				"tags", "tag2"
+				);
+		method.doMethod("persons", "Hallvard", "geoLocations", "0");
+		Assert.assertEquals("geoLocs2", dbAccessRecord.get("updateGeoLocationsData"));
+		Assert.assertEquals("geoLocs2", geoLocations1.getName());
+		Assert.assertEquals("my second geo-location", geoLocations1.getDescription());
+		Assert.assertEquals(LocalDate.of(2018, 3, 15), geoLocations1.getDate());
+		Assert.assertEquals(LocalTime.of(11, 15), geoLocations1.getTime());
+		Assert.assertEquals(ZoneId.of("Europe/Paris"), geoLocations1.getZone());
+		Assert.assertEquals("tag2", geoLocations1.getTags(null, ",", null));
+	}
+
+
+	@Test
+	// delete Person
+	public void testDeleteGeoLocations() {
+		final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no");
+		final GeoLocations geoLocations1 = dbAccess.createGeoLocations(person);
+		geoLocations1.setName("geoLoc1");
+		final GeoLocations geoLocations2 = dbAccess.createGeoLocations(person);
+		geoLocations2.setName("geoLoc2");
+		final HttpMethod method = new DeleteMethod(rootEntity);
+		method.doMethod("persons", "Hallvard", "geoLocations", "-2");
+		Assert.assertEquals("geoLoc1", dbAccessRecord.get("deleteGeoLocations"));
+		Assert.assertEquals(1, person.getGeoLocations((String[]) null).size());
+		method.doMethod("persons", "Hallvard", "geoLocations", "0");
+		Assert.assertEquals("geoLoc2", dbAccessRecord.get("deleteGeoLocations"));
+		Assert.assertEquals(0, person.getGeoLocations((String[]) null).size());
+	}
+}