Skip to content

Commit

Permalink
[Feature #28] Pass custom deserializers to the deserialization process.
Browse files Browse the repository at this point in the history
  • Loading branch information
ledsoft committed Mar 29, 2022
1 parent 9544e89 commit 1fc94ab
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* Copyright (C) 2022 Czech Technical University in Prague
*
* <p>
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
Expand Down Expand Up @@ -36,6 +36,8 @@ public abstract class JsonLdDeserializer implements Configured {

protected final TargetClassResolver classResolver;

protected ValueDeserializers deserializers = new CommonValueDeserializers();

protected JsonLdDeserializer() {
this.configuration = new Configuration();
this.classResolver = initializeTargetClassResolver();
Expand Down Expand Up @@ -67,6 +69,21 @@ public Configuration configuration() {
return configuration;
}

/**
* Registers a custom deserializer for the specified type.
* <p>
* If a deserializer already existed for the type, it is replaced by the new one.
*
* @param type Target type to register the deserializer for
* @param deserializer Deserializer to register
* @param <T> Target type
*/
public <T> void registerSerializer(Class<T> type, ValueDeserializer<T> deserializer) {
Objects.requireNonNull(type);
Objects.requireNonNull(deserializer);
deserializers.registerDeserializer(type, deserializer);
}

/**
* Deserializes the specified JSON-LD data.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* Copyright (C) 2022 Czech Technical University in Prague
*
* <p>
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
Expand All @@ -15,16 +15,19 @@
package cz.cvut.kbss.jsonld.deserialization.expanded;

import cz.cvut.kbss.jsonld.Configuration;
import cz.cvut.kbss.jsonld.deserialization.ValueDeserializers;
import cz.cvut.kbss.jsonld.deserialization.util.TargetClassResolver;

class DeserializerConfig {

private final Configuration configuration;
private final TargetClassResolver targetResolver;
private final ValueDeserializers deserializers;

DeserializerConfig(Configuration configuration, TargetClassResolver targetResolver) {
DeserializerConfig(Configuration configuration, TargetClassResolver targetResolver, ValueDeserializers deserializers) {
this.configuration = configuration;
this.targetResolver = targetResolver;
this.deserializers = deserializers;
}

Configuration getConfiguration() {
Expand All @@ -34,4 +37,8 @@ Configuration getConfiguration() {
TargetClassResolver getTargetResolver() {
return targetResolver;
}

ValueDeserializers getDeserializers() {
return deserializers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ public <T> T deserialize(Object jsonLd, Class<T> resultClass) {
throw new JsonLdDeserializationException(
"Input is not expanded JSON-LD. The input does not contain exactly one root element.");
}
deserializers.configure(configuration());
final Map<?, ?> root = (Map<?, ?>) input.get(0);
final PendingReferenceRegistry referenceRegistry = new PendingReferenceRegistry();
final InstanceBuilder instanceBuilder = new DefaultInstanceBuilder(classResolver, referenceRegistry);
new ObjectDeserializer(instanceBuilder, new DeserializerConfig(configuration(), classResolver), resultClass)
new ObjectDeserializer(instanceBuilder, new DeserializerConfig(configuration(), classResolver, deserializers), resultClass)
.processValue(root);
referenceRegistry.verifyNoUnresolvedReferencesExist();
assert resultClass.isAssignableFrom(instanceBuilder.getCurrentRoot().getClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public abstract class JsonLdSerializer implements Configured {

private final Configuration configuration;

final JsonGenerator jsonGenerator;
protected final JsonGenerator jsonGenerator;

final ValueSerializers serializers = new CommonValueSerializers();
protected final ValueSerializers serializers = new CommonValueSerializers();

protected JsonLdSerializer(JsonGenerator jsonGenerator) {
this.jsonGenerator = Objects.requireNonNull(jsonGenerator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import cz.cvut.kbss.jopa.vocabulary.RDFS;
import cz.cvut.kbss.jsonld.Configuration;
import cz.cvut.kbss.jsonld.JsonLd;
import cz.cvut.kbss.jsonld.deserialization.CommonValueDeserializers;
import cz.cvut.kbss.jsonld.deserialization.DefaultInstanceBuilder;
import cz.cvut.kbss.jsonld.deserialization.InstanceBuilder;
import cz.cvut.kbss.jsonld.deserialization.reference.PendingReferenceRegistry;
Expand Down Expand Up @@ -52,7 +53,7 @@ class CollectionDeserializerTest {
void setUp() {
final TargetClassResolver typeResolver = new TargetClassResolver(new TypeMap());
this.instanceBuilder = new DefaultInstanceBuilder(typeResolver, new PendingReferenceRegistry());
this.deserializerConfig = new DeserializerConfig(new Configuration(), typeResolver);
this.deserializerConfig = new DeserializerConfig(new Configuration(), typeResolver, new CommonValueDeserializers());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package cz.cvut.kbss.jsonld.deserialization.expanded;

import cz.cvut.kbss.jsonld.Configuration;
import cz.cvut.kbss.jsonld.deserialization.CommonValueDeserializers;
import cz.cvut.kbss.jsonld.deserialization.util.TargetClassResolver;
import cz.cvut.kbss.jsonld.deserialization.util.TypeMap;
import cz.cvut.kbss.jsonld.environment.TestUtil;
Expand All @@ -31,7 +32,7 @@ class DeserializerTest {
void resolveTargetClassReturnsExpectedClassWhenItIsPlainIdentifierType() throws Exception {
final Deserializer<Map<?, ?>> deserializer =
new ObjectDeserializer(null,
new DeserializerConfig(new Configuration(), new TargetClassResolver(new TypeMap())),
new DeserializerConfig(new Configuration(), new TargetClassResolver(new TypeMap()), new CommonValueDeserializers()),
URI.class);
assertEquals(URI.class,
deserializer.resolveTargetClass(TestUtil.readAndExpand("objectWithDataProperties.json"), URI.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import cz.cvut.kbss.jsonld.Configuration;
import cz.cvut.kbss.jsonld.JsonLd;
import cz.cvut.kbss.jsonld.annotation.JsonLdAttributeOrder;
import cz.cvut.kbss.jsonld.deserialization.CommonValueDeserializers;
import cz.cvut.kbss.jsonld.deserialization.InstanceBuilder;
import cz.cvut.kbss.jsonld.deserialization.ValueDeserializers;
import cz.cvut.kbss.jsonld.deserialization.util.TargetClassResolver;
import cz.cvut.kbss.jsonld.environment.TestUtil;
import cz.cvut.kbss.jsonld.environment.Vocabulary;
Expand Down Expand Up @@ -57,6 +59,8 @@ class ObjectDeserializerTest {
@Mock
private TargetClassResolver tcResolverMock;

private final ValueDeserializers deserializers = new CommonValueDeserializers();

private ObjectDeserializer sut;

@BeforeEach
Expand All @@ -75,7 +79,7 @@ void processValueProcessesAttributesInOrderSpecifiedByJsonLdAttributeOrder() thr
doAnswer(inv -> Study.class).when(instanceBuilderMock).getCurrentContextType();
doAnswer(inv -> Employee.class).when(instanceBuilderMock).getCurrentCollectionElementType();
this.sut =
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock),
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock, deserializers),
Study.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithPluralReferenceSharingObject.json");
sut.processValue((Map<?, ?>) input.get(0));
Expand All @@ -95,7 +99,7 @@ void processValueThrowsJsonLdDeserializationExceptionWhenUnknownFieldNameIsUsedI
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(true);
doAnswer(inv -> InvalidOrder.class).when(instanceBuilderMock).getCurrentContextType();
this.sut =
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock),
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock, deserializers),
InvalidOrder.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithPluralReferenceSharingObject.json");

Expand All @@ -122,7 +126,7 @@ void processValueOpensObjectWithId() throws Exception {
doReturn(User.class).when(tcResolverMock).getTargetClass(eq(User.class), anyCollection());
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(true);
this.sut =
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock),
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock, deserializers),
User.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithDataProperties.json");
sut.processValue((Map<?, ?>) input.get(0));
Expand All @@ -134,7 +138,7 @@ void processValueOpensObjectAsAttributeValueWithId() throws Exception {
doReturn(User.class).when(tcResolverMock).getTargetClass(eq(User.class), anyCollection());
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(true);
this.sut =
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock),
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock, deserializers),
User.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithSingularReference.json");
sut.processValue((Map<?, ?>) input.get(0));
Expand All @@ -148,7 +152,7 @@ void processValueDoesNotAddIdToInstanceBuilder() throws Exception {
doReturn(User.class).when(tcResolverMock).getTargetClass(eq(User.class), anyCollection());
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(true);
this.sut =
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock),
new ObjectDeserializer(instanceBuilderMock, new DeserializerConfig(new Configuration(), tcResolverMock, deserializers),
User.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithDataProperties.json");
sut.processValue((Map<?, ?>) input.get(0));
Expand All @@ -161,7 +165,7 @@ void processValueGeneratesIdWhenIncomingObjectDoesNotContainIt() throws Exceptio
doReturn(User.class).when(tcResolverMock).getTargetClass(eq(User.class), anyCollection());
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(true);
this.sut = new ObjectDeserializer(instanceBuilderMock,
new DeserializerConfig(new Configuration(), tcResolverMock), User.class);
new DeserializerConfig(new Configuration(), tcResolverMock, deserializers), User.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithDataProperties.json");
((Map<?, ?>) input.get(0)).remove(JsonLd.ID);
sut.processValue((Map<?, ?>) input.get(0));
Expand All @@ -178,7 +182,7 @@ void processValueSkipsPropertyMappedToFieldWithReadOnlyAccess() throws Exception
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(true);
when(instanceBuilderMock.isPropertyDeserializable(eq(Vocabulary.NUMBER_OF_PEOPLE_INVOLVED))).thenReturn(false);
this.sut = new ObjectDeserializer(instanceBuilderMock,
new DeserializerConfig(new Configuration(), tcResolverMock), Study.class);
new DeserializerConfig(new Configuration(), tcResolverMock, deserializers), Study.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithReadOnlyPropertyValue.json");
((Map<?, ?>) input.get(0)).remove(Vocabulary.HAS_MEMBER);
((Map<?, ?>) input.get(0)).remove(Vocabulary.HAS_PARTICIPANT);
Expand All @@ -195,7 +199,7 @@ void processValueIgnoresMissingIdFieldWhenIgnoreUnknownPropertiesIsConfigured()
final Configuration config = new Configuration();
config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.TRUE.toString());
this.sut = new ObjectDeserializer(instanceBuilderMock,
new DeserializerConfig(config, tcResolverMock), Object.class);
new DeserializerConfig(config, tcResolverMock, deserializers), Object.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithDataProperties.json");
sut.processValue((Map<?, ?>) input.get(0));
}
Expand All @@ -208,7 +212,7 @@ void processValueThrowsUnknownPropertyExceptionWhenObjectIsMissingIdField() thro
doThrow(ex).when(instanceBuilderMock).openObject(anyString(), any(Class.class));
when(instanceBuilderMock.isPropertyDeserializable(any())).thenReturn(false);
this.sut = new ObjectDeserializer(instanceBuilderMock,
new DeserializerConfig(new Configuration(), tcResolverMock), Object.class);
new DeserializerConfig(new Configuration(), tcResolverMock, deserializers), Object.class);
final List<?> input = (List<?>) TestUtil.readAndExpand("objectWithDataProperties.json");
final UnknownPropertyException result = assertThrows(UnknownPropertyException.class,
() -> sut.processValue((Map<?, ?>) input.get(0)));
Expand Down

0 comments on commit 1fc94ab

Please sign in to comment.