From 35e595ef5e5ba8631c7f6f9d8da73ccb74436587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Bergst=C3=B8l?= Date: Wed, 17 Apr 2024 12:38:09 +0200 Subject: [PATCH] Parse DataType from json string to object without hinting of type So, the problem is that when a DataType is serialized into json, the data structure that is the result has no type-information other than a property type="ImplementationType" in the json. To be able to read json into a Java Object we do need to tell the objectReader what we want in return. Either you parse the json into a Map and check the magic property, or you store the type along side the json and use that to tell the objectMapper what type you want. This change annotates the DataType interface with information on what property should be checked for type information and what implementations that are in existence. This gives us the ability to read a json text into a DataType without knowing the type it should result in. --- NOTICE | 1 + pom.xml | 18 ++++----- .../no/digipost/api/datatypes/DataType.java | 39 +++++++++++++++++++ .../api/datatypes/DataTypeIdentifier.java | 28 ++++++------- .../marshalling/MarshallingTest.java | 2 +- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/NOTICE b/NOTICE index d034815e..6a2fc7ba 100644 --- a/NOTICE +++ b/NOTICE @@ -13,6 +13,7 @@ under the License. This project includes: Angus Activation Registries under EDL 1.0 + Byte Buddy (without dependencies) under Apache License, Version 2.0 ClassMate under Apache License, Version 2.0 Digipost Data Types under The Apache Software License, Version 2.0 Eclipse Expressly under Eclipse Public License v. 2.0 or GNU General Public License, version 2 with the GNU Classpath Exception diff --git a/pom.xml b/pom.xml index b3c6da3b..a5f93cb3 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 11 11 - 1.18.30 + 1.18.32 @@ -24,14 +24,14 @@ com.fasterxml.jackson jackson-bom - 2.16.0 + 2.17.0 pom import org.junit junit-bom - 5.5.2 + 5.10.2 pom import @@ -69,7 +69,7 @@ org.hibernate.validator hibernate-validator - 8.0.0.Final + 8.0.1.Final runtime true @@ -90,12 +90,12 @@ jakarta.xml.bind jakarta.xml.bind-api - 4.0.1 + 4.0.2 org.glassfish.jaxb jaxb-runtime - 4.0.4 + 4.0.5 runtime @@ -135,7 +135,7 @@ org.slf4j slf4j-simple - 2.0.7 + 2.0.13 test @@ -272,12 +272,12 @@ jakarta.xml.bind jakarta.xml.bind-api - 4.0.1 + 4.0.2 org.glassfish.jaxb jaxb-runtime - 4.0.4 + 4.0.5 runtime diff --git a/src/main/java/no/digipost/api/datatypes/DataType.java b/src/main/java/no/digipost/api/datatypes/DataType.java index b47d398f..da4916b5 100644 --- a/src/main/java/no/digipost/api/datatypes/DataType.java +++ b/src/main/java/no/digipost/api/datatypes/DataType.java @@ -2,7 +2,46 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import no.digipost.api.datatypes.types.Appointment; +import no.digipost.api.datatypes.types.Boligdetaljer; +import no.digipost.api.datatypes.types.Event; +import no.digipost.api.datatypes.types.ExternalLink; +import no.digipost.api.datatypes.types.Inkasso; +import no.digipost.api.datatypes.types.Payslip; +import no.digipost.api.datatypes.types.Residence; +import no.digipost.api.datatypes.types.SignedDocument; +import no.digipost.api.datatypes.types.invoice.Invoice; +import no.digipost.api.datatypes.types.invoice.InvoicePayment; +import no.digipost.api.datatypes.types.pickup.PickupNotice; +import no.digipost.api.datatypes.types.pickup.PickupNoticeStatus; +import no.digipost.api.datatypes.types.proof.Proof; +import no.digipost.api.datatypes.types.receipt.Receipt; +import no.digipost.api.datatypes.types.share.ShareDocumentsRequest; +import no.digipost.api.datatypes.types.share.ShareDocumentsRequestDocumentsShared; +import no.digipost.api.datatypes.types.share.ShareDocumentsRequestSharingStopped; +@JsonTypeInfo(use = JsonTypeInfo.Id.SIMPLE_NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(Appointment.class) + , @JsonSubTypes.Type(Residence.class) + , @JsonSubTypes.Type(ExternalLink.class) + , @JsonSubTypes.Type(Boligdetaljer.class) + , @JsonSubTypes.Type(Receipt.class) + , @JsonSubTypes.Type(Payslip.class) + , @JsonSubTypes.Type(SignedDocument.class) + , @JsonSubTypes.Type(PickupNotice.class) + , @JsonSubTypes.Type(PickupNoticeStatus.class) + , @JsonSubTypes.Type(Event.class) + , @JsonSubTypes.Type(Proof.class) + , @JsonSubTypes.Type(Inkasso.class) + , @JsonSubTypes.Type(Invoice.class) + , @JsonSubTypes.Type(InvoicePayment.class) + , @JsonSubTypes.Type(ShareDocumentsRequest.class) + , @JsonSubTypes.Type(ShareDocumentsRequestSharingStopped.class) + , @JsonSubTypes.Type(ShareDocumentsRequestDocumentsShared.class) +}) public interface DataType { @JsonProperty("type") diff --git a/src/main/java/no/digipost/api/datatypes/DataTypeIdentifier.java b/src/main/java/no/digipost/api/datatypes/DataTypeIdentifier.java index 785e756a..9a89a25f 100644 --- a/src/main/java/no/digipost/api/datatypes/DataTypeIdentifier.java +++ b/src/main/java/no/digipost/api/datatypes/DataTypeIdentifier.java @@ -77,19 +77,11 @@ public enum DataTypeIdentifier { this.example = example; complementables = Optional.ofNullable(getDataType().getAnnotation(ComplementedBy.class)) .map(ComplementedBy::value) - .map(Stream::of) - .orElseGet(Stream::empty) + .stream() + .flatMap(Stream::of) .collect(collectingAndThen(toSet(), Collections::unmodifiableSet)); } - public Class getDataType() { - return dataType; - } - - public String getShortName() { - return shortName; - } - public static DataTypeIdentifier fromRepresentationType(final Class representation) { return ofNullable(byType.get(representation)) .orElseThrow(() -> new IllegalStateException(String.format( @@ -103,10 +95,6 @@ public static DataTypeIdentifier fromShortName(final String shortName) { .orElseThrow(() -> new IllegalArgumentException("No value for " + DataTypeIdentifier.class.getSimpleName() + " found for shortName " + shortName)); } - public DataType getExample() { - return example; - } - public static Set> getAllClasses() { return byType.keySet(); } @@ -114,4 +102,16 @@ public static Set> getAllClasses() { public boolean canBeComplementedBy(DataType successor) { return complementables.contains(successor.getClass()); } + + public Class getDataType() { + return dataType; + } + + public String getShortName() { + return shortName; + } + + public DataType getExample() { + return example; + } } diff --git a/src/test/java/no/digipost/api/datatypes/marshalling/MarshallingTest.java b/src/test/java/no/digipost/api/datatypes/marshalling/MarshallingTest.java index 6e47fc38..0621a281 100644 --- a/src/test/java/no/digipost/api/datatypes/marshalling/MarshallingTest.java +++ b/src/test/java/no/digipost/api/datatypes/marshalling/MarshallingTest.java @@ -36,7 +36,7 @@ static void testJacksonJsonMarshalling(DataTypeIdentifier example) { try { ObjectMapper mapper = DataTypesJsonMapper.getMapper(); final String json = mapper.writer().writeValueAsString(example.getExample()); - final DataType unmarshalled = mapper.reader().forType(example.getDataType()).readValue(json); + final DataType unmarshalled = mapper.reader().forType(DataType.class).readValue(json); assertThat(unmarshalled, equalTo(example.getExample())); } catch (IOException e) { throw new RuntimeException(e);