package com.codepoetics.octarine.json; import com.codepoetics.octarine.functional.paths.Path; import com.codepoetics.octarine.json.deserialisation.ListDeserialiser; import com.codepoetics.octarine.json.deserialisation.MapDeserialiser; import com.codepoetics.octarine.json.deserialisation.RecordDeserialiser; import com.codepoetics.octarine.json.example.Address; import com.codepoetics.octarine.json.example.Person; import com.codepoetics.octarine.records.Key; import com.codepoetics.octarine.records.ListKey; import com.codepoetics.octarine.records.Record; import com.codepoetics.octarine.records.RecordKey; import com.codepoetics.octarine.records.Valid; import com.codepoetics.octarine.records.Validation; import com.codepoetics.octarine.testutils.ARecord; import com.codepoetics.octarine.testutils.AnInstance; import com.codepoetics.octarine.testutils.Present; import org.junit.Test; import org.pcollections.PMap; import org.pcollections.PVector; import java.awt.*; import java.util.List; import java.util.Map; import static com.codepoetics.octarine.Octarine.*; import static com.codepoetics.octarine.functional.paths.Path.toIndex; import static com.codepoetics.octarine.testutils.IsEmptyMatcher.isEmpty; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; public class DeserialisationTest { private static final Key<String> prefix = $("prefix"); private static final Key<String> number = $("number"); private static final RecordDeserialiser readNumber = RecordDeserialiser.builder() .readString(prefix) .readString(number) .get(); private static final Key<PMap<String, Record>> numbers = $M("numbers"); private static final RecordDeserialiser readNumbers = RecordDeserialiser.builder() .readMap(numbers, readNumber) .get(); @Test public void deserialises_json_to_record() { Valid<Person> person = Person.deserialiser.validAgainst(Person.schema).fromString(String.join("\n", "{", " \"name\": \"Dominic\",", " \"age\": 39,", " \"favourite colour\": \"0xFF0000\",", " \"address\": {", " \"addressLines\": [", " \"13 Rue Morgue\",", " \"PO3 1TP\"", " ]", " }", "}") ).get(); assertThat(person, ARecord.instance() .with(Person.name, "Dominic") .with(Person.age, 39) .with(Person.favouriteColour, Color.RED) .with(Person.address.join(Address.addressLines).join(toIndex(1)), "PO3 1TP")); } @Test public void returns_nested_validation_exceptions() { Validation<Person> personResult = Person.deserialiser.validAgainst(Person.schema).fromString(String.join("\n", "{", " \"name\": \"Dominic\",", " \"age\": 39,", " \"favourite colour\": \"0xFF0000\",", " \"address\": {", " \"addressLinez\": [", " \"13 Rue Morgue\",", " \"PO3 1TP\"", " ]", " }", "}")); assertThat(personResult.isValid(), equalTo(false)); assertThat(personResult.validationErrors(), hasItem(containsString("addressLines"))); } @Test public void handles_empty_arrays() { assertThat(Address.deserialiser.fromString("{\"addressLines\":[]}"), ARecord.instance().with(Address.addressLines, isEmpty())); } @SuppressWarnings("unchecked") @Test public void handles_arrays_of_empty_arrays() { Key<PVector<PVector<String>>> key = $L("emptiness"); RecordDeserialiser deserialiser = RecordDeserialiser.builder().readList(key, ListDeserialiser.readingStrings()).get(); assertThat(deserialiser.fromString("{\"emptiness\": [[],[],[]]}"), ARecord.instance().with(key, hasItems(isEmpty(), isEmpty(), isEmpty()))); } @Test public void handles_arrays_of_objects() { ListKey<Record> addresses = $L("addresses"); RecordDeserialiser deserialiser = RecordDeserialiser.builder().readList(addresses, Address.deserialiser).get(); Record r = deserialiser.fromString("{\"addresses\":[{\"addressLines\":[\"line 1\",\"line 2\"]},{\"addressLines\":[]}]}"); assertThat(r, ARecord.instance() .with(addresses.join(toIndex(0)).join(Address.addressLines).join(toIndex(0)), "line 1") .with(addresses.join(toIndex(0)).join(Address.addressLines).join(toIndex(1)), "line 2") .with(addresses.join(toIndex(1)).join(Address.addressLines), isEmpty())); } @Test public void handles_arrays_of_arrays() { ListKey<PVector<Integer>> rows = $L("rows"); RecordDeserialiser deserialiser = RecordDeserialiser.builder().readList(rows, ListDeserialiser.readingIntegers()).get(); Record r = deserialiser.fromString("{\"rows\":[[1,2,3],[4,5,6],[7,8,9]]}"); assertThat(rows.join(toIndex(1)).join(toIndex(1)).apply(r).get(), equalTo(5)); } @Test public void can_deserialise_a_list_of_records() { String json = "[{\"prefix\": \"0208\", \"number\": \"123456\"}, {\"prefix\": \"07775\", \"number\": \"654321\"}]"; List<Record> numbers = ListDeserialiser.readingItemsWith(readNumber).fromString(json); assertThat(numbers, AnInstance.<List<Record>>ofGeneric(List.class) .with(Path.<Record>toIndex(0).join(prefix), Present.and(equalTo("0208")))); } @Test public void can_deserialise_a_map_of_records() { String json = "{\"home\": {\"prefix\": \"0208\", \"number\": \"123456\"}, \"work\": {\"prefix\": \"07775\", \"number\": \"654321\"}}"; Map<String, Record> numbers = MapDeserialiser.readingValuesWith(readNumber).fromString(json); assertThat(numbers, AnInstance.<Map<String, Record>>ofGeneric(Map.class) .with(Path.<String, Record>toKey("home").join(prefix), Present.and(equalTo("0208")))); } @Test public void can_deserialise_a_map_of_maps_of_records() { String json = "{\"no-one\": {}, " + "\"dominic\": {\"home\": {\"prefix\": \"0208\", \"number\": \"123456\"}, " + "\"work\": {\"prefix\": \"07775\", \"number\": \"654321\"}}}"; Map<String, PMap<String, Record>> numbers = MapDeserialiser.readingValuesWith(MapDeserialiser.readingValuesWith(readNumber)).fromString(json); assertThat(numbers, AnInstance.<Map<String, PMap<String, Record>>>ofGeneric(Map.class) .with(Path.<String, PMap<String, Record>>toKey("dominic").join(Path.<String, Record>toKey("home")).join(prefix), Present.and(equalTo("0208")))); } @Test public void can_deserialise_a_record_containing_a_map_of_records() { String json = "{\"numbers\": {\"home\": {\"prefix\": \"0208\", \"number\": \"123456\"}, \"work\": {\"prefix\": \"07775\", \"number\": \"654321\"}}}"; Record theNumbers = readNumbers.fromString(json); assertThat(theNumbers, ARecord.instance() .with(numbers.join(Path.toKey("home")).join(prefix), "0208")); } @Test public void can_deserialise_a_record_containing_explicit_nulls() { String json = "{\"numbers\": {\"home\": {\"prefix\": null, \"number\": \"123456\"}}}"; Record theNumbers = readNumbers.fromString(json); assertThat(theNumbers, ARecord.instance() .with(numbers.join(Path.toKey("home")).join(number), "123456")); } @Test public void can_deserialise_records_containing_keys_with_same_name() { String json = String.join("\n", "{", " \"id\": \"12\",", " \"child\": {", " \"id\": \"34\",", " \"name\": \"Fred\"", " }", "}"); Key<String> childId = $("id"); Key<String> name = $("name"); RecordDeserialiser readChild = RecordDeserialiser.builder() .readString(childId) .readString(name) .get(); Key<String> id = $("id"); RecordKey child = $R("child"); RecordDeserialiser readNested = RecordDeserialiser.builder() .readString(id) .read(child, readChild) .get(); Record theNested = readNested.fromString(json); assertThat(theNested, ARecord.instance() .with(id, "12") .with(child.join(name), "Fred") .with(child.join(childId), "34") ); } @Test public void can_deserialise_records_with_ignored_children() { String json = String.join("\n", "{", " \"id\": \"12\",", " \"dontcare\": {", " \"id\": \"56\",", " \"name\": \"Don't want\"", " }", "}"); Key<String> id = $("id"); RecordDeserialiser readNested = RecordDeserialiser.builder() .readString(id) .get(); Record theNested = readNested.fromString(json); assertThat(theNested, ARecord.instance().with(id, "12")); } }