Skip to content

Commit 273985a

Browse files
committed
[Enhancement #54] Implement serialization of individuals as string with extended term definition in context.
1 parent ace34ee commit 273985a

19 files changed

+302
-111
lines changed

src/main/java/cz/cvut/kbss/jsonld/ConfigParam.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,15 @@ public enum ConfigParam {
9999
* <p>
100100
* Also note that this format applies only to full datetime values. Date or time values have to be formatted per-attribute.
101101
*/
102-
DATE_TIME_FORMAT("datetimeFormat");
102+
DATE_TIME_FORMAT("datetimeFormat"),
103+
104+
/**
105+
* Whether to serialize individuals using expanded term definition in context.
106+
*
107+
* This basically means that the individual's identifier is provided directly as a string and an expanded term
108+
* definition is added into the context, specifying that the string is an identifier.
109+
*/
110+
SERIALIZE_INDIVIDUALS_USING_EXPANDED_DEFINITION("serializeIndividualsUsingExpandedDefinition");
103111

104112
private final String name;
105113

src/main/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializer.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ protected ValueSerializers initSerializers() {
5050
new LiteralValueSerializers(new DefaultValueSerializer(new MultilingualStringSerializer()));
5151
valueSerializers.registerIdentifierSerializer(new IdentifierSerializer());
5252
valueSerializers.registerTypesSerializer(new TypesSerializer());
53+
valueSerializers.registerIndividualSerializer(new IndividualSerializer());
5354
final TemporalSerializer ts = new TemporalSerializer();
5455
valueSerializers.registerSerializer(LocalDate.class, ts);
5556
// Register the same temporal serializer for each of the types it supports (needed for key-based map access)
@@ -72,12 +73,16 @@ protected JsonNode buildJsonTree(Object root) {
7273
final ObjectGraphTraverser traverser = new ObjectGraphTraverser(new SerializationContextFactory(
7374
DummyJsonLdContext.INSTANCE));
7475
traverser.setRequireId(configuration().is(ConfigParam.REQUIRE_ID));
75-
final JsonLdTreeBuilder treeBuilder =
76-
new JsonLdTreeBuilder(
77-
new ObjectGraphValueSerializers(serializers, new ObjectPropertyValueSerializer(traverser)),
78-
DummyJsonLdContext.INSTANCE);
76+
final JsonLdTreeBuilder treeBuilder = initTreeBuilder(traverser);
7977
traverser.setVisitor(treeBuilder);
8078
traverser.traverse(root);
8179
return treeBuilder.getTreeRoot();
8280
}
81+
82+
private JsonLdTreeBuilder initTreeBuilder(ObjectGraphTraverser traverser) {
83+
final ObjectPropertyValueSerializer opSerializer = new ObjectPropertyValueSerializer(traverser);
84+
opSerializer.configure(configuration());
85+
return new JsonLdTreeBuilder(new ObjectGraphValueSerializers(serializers, opSerializer),
86+
DummyJsonLdContext.INSTANCE);
87+
}
8388
}

src/main/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializer.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ protected ValueSerializers initSerializers() {
5050
new LiteralValueSerializers(new ContextBuildingDefaultValueSerializer(mlsSerializer, mlsColSerializer));
5151
valueSerializers.registerIdentifierSerializer(new ContextBuildingIdentifierSerializer());
5252
valueSerializers.registerTypesSerializer(new ContextBuildingTypesSerializer());
53+
valueSerializers.registerIndividualSerializer(new ContextBuildingIndividualSerializer());
5354
final ContextBuildingTemporalSerializer ts = new ContextBuildingTemporalSerializer();
5455
valueSerializers.registerSerializer(LocalDate.class, ts);
5556
// Register the same temporal serializer for each of the types it supports (needed for key-based map access)
@@ -96,9 +97,9 @@ private void ensureContextNodeNotPresent(CompositeNode<?> root, JsonNode rootCtx
9697

9798
private JsonLdTreeBuilder initTreeBuilder(ObjectGraphTraverser traverser,
9899
JsonLdContextFactory jsonLdContextFactory) {
99-
return new JsonLdTreeBuilder(new ObjectGraphValueSerializers(serializers,
100-
new ContextBuildingObjectPropertyValueSerializer(
101-
traverser)), jsonLdContextFactory);
100+
final ContextBuildingObjectPropertyValueSerializer opSerializer = new ContextBuildingObjectPropertyValueSerializer(traverser);
101+
opSerializer.configure(configuration());
102+
return new JsonLdTreeBuilder(new ObjectGraphValueSerializers(serializers, opSerializer), jsonLdContextFactory);
102103
}
103104

104105
private JsonNode buildObjectWithContextAndGraph(ObjectGraphTraverser traverser, JsonLdContext rootContext,

src/main/java/cz/cvut/kbss/jsonld/serialization/JsonLdTreeBuilder.java

+12
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ public JsonLdTreeBuilder(ValueSerializers serializers, JsonLdContextFactory json
4343
this.jsonLdContextFactory = jsonLdContextFactory;
4444
}
4545

46+
@Override
47+
public void visitIndividual(SerializationContext<?> ctx) {
48+
final ValueSerializer s = serializers.getIndividualSerializer();
49+
final JsonNode node = s.serialize(ctx.getValue(), ctx);
50+
if (currentNode != null) {
51+
currentNode.addItem(node);
52+
} else {
53+
assert node instanceof CompositeNode;
54+
currentNode = (CompositeNode<?>) node;
55+
}
56+
}
57+
4658
@Override
4759
public boolean visitObject(SerializationContext<?> ctx) {
4860
if (serializers.hasCustomSerializer(ctx.getValue().getClass())) {

src/main/java/cz/cvut/kbss/jsonld/serialization/model/JsonNode.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import cz.cvut.kbss.jsonld.serialization.JsonGenerator;
1919

2020
import java.io.IOException;
21-
import java.util.Objects;
2221

2322
public abstract class JsonNode {
2423

@@ -31,8 +30,8 @@ public abstract class JsonNode {
3130
}
3231

3332
public JsonNode(String name) {
34-
this.name = Objects.requireNonNull(name);
35-
this.valueNode = false;
33+
this.name = name;
34+
this.valueNode = name == null;
3635
}
3736

3837
public String getName() {

src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/LiteralValueSerializers.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ public class LiteralValueSerializers implements ValueSerializers {
3434

3535
private ValueSerializer<Set<String>> typesSerializer;
3636

37+
private ValueSerializer<?> individualSerializer;
38+
3739
public LiteralValueSerializers(ValueSerializer<?> defaultSerializer) {
3840
this.defaultSerializer = Objects.requireNonNull(defaultSerializer);
3941
}
4042

41-
4243
@Override
4344
public <T> boolean hasCustomSerializer(Class<T> type) {
4445
return serializers.containsKey(type);
@@ -81,8 +82,22 @@ public void registerTypesSerializer(ValueSerializer<Set<String>> typesSerializer
8182
this.typesSerializer = Objects.requireNonNull(typesSerializer);
8283
}
8384

85+
@Override
86+
public ValueSerializer<?> getIndividualSerializer() {
87+
return individualSerializer;
88+
}
89+
90+
@Override
91+
public void registerIndividualSerializer(ValueSerializer<?> individualSerializer) {
92+
this.individualSerializer = Objects.requireNonNull(individualSerializer);
93+
}
94+
8495
@Override
8596
public void configure(Configuration configuration) {
8697
serializers.values().forEach(vs -> vs.configure(configuration));
98+
individualSerializer.configure(configuration);
99+
identifierSerializer.configure(configuration);
100+
typesSerializer.configure(configuration);
101+
defaultSerializer.configure(configuration);
87102
}
88103
}

src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/ObjectGraphValueSerializers.java

+10
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,14 @@ public ValueSerializer<Set<String>> getTypesSerializer() {
8080
public void registerTypesSerializer(ValueSerializer<Set<String>> typesSerializer) {
8181
serializers.registerTypesSerializer(typesSerializer);
8282
}
83+
84+
@Override
85+
public ValueSerializer<?> getIndividualSerializer() {
86+
return serializers.getIndividualSerializer();
87+
}
88+
89+
@Override
90+
public void registerIndividualSerializer(ValueSerializer<?> individualSerializer) {
91+
serializers.registerIndividualSerializer(individualSerializer);
92+
}
8393
}

src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/ValueSerializers.java

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package cz.cvut.kbss.jsonld.serialization.serializer;
1414

1515
import cz.cvut.kbss.jsonld.common.Configurable;
16+
import cz.cvut.kbss.jsonld.serialization.serializer.compact.IndividualSerializer;
1617
import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext;
1718

1819
import java.util.Optional;
@@ -69,4 +70,8 @@ public interface ValueSerializers extends Configurable {
6970
ValueSerializer<Set<String>> getTypesSerializer();
7071

7172
void registerTypesSerializer(ValueSerializer<Set<String>> typesSerializer);
73+
74+
ValueSerializer<?> getIndividualSerializer();
75+
76+
void registerIndividualSerializer(ValueSerializer<?> individualSerializer);
7277
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cz.cvut.kbss.jsonld.serialization.serializer.compact;
2+
3+
import cz.cvut.kbss.jsonld.JsonLd;
4+
import cz.cvut.kbss.jsonld.common.BeanClassProcessor;
5+
import cz.cvut.kbss.jsonld.common.EnumUtil;
6+
import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory;
7+
import cz.cvut.kbss.jsonld.serialization.model.JsonNode;
8+
import cz.cvut.kbss.jsonld.serialization.model.ObjectNode;
9+
import cz.cvut.kbss.jsonld.serialization.serializer.ValueSerializer;
10+
import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext;
11+
12+
/**
13+
* Serializes individuals.
14+
* <p>
15+
* That is, either a plain identifier value of an object property attribute or an enum constant mapped to an
16+
* individual.
17+
*/
18+
public class IndividualSerializer implements ValueSerializer {
19+
20+
@Override
21+
public JsonNode serialize(Object value, SerializationContext ctx) {
22+
assert BeanClassProcessor.isIdentifierType(value.getClass()) || value.getClass().isEnum();
23+
if (BeanClassProcessor.isIdentifierType(value.getClass())) {
24+
final ObjectNode node = JsonNodeFactory.createObjectNode(ctx.getTerm());
25+
node.addItem(JsonNodeFactory.createObjectIdNode(idAttribute(ctx), value));
26+
return node;
27+
} else {
28+
return serializeEnumConstant((Enum<?>) value, ctx);
29+
}
30+
}
31+
32+
private String idAttribute(SerializationContext<?> ctx) {
33+
return ctx.getJsonLdContext().getMappedTerm(JsonLd.ID).orElse(JsonLd.ID);
34+
}
35+
36+
protected JsonNode serializeEnumConstant(Enum<?> constant, SerializationContext<?> ctx) {
37+
final String iri = EnumUtil.resolveMappedIndividual(constant);
38+
final ObjectNode node = JsonNodeFactory.createObjectNode(ctx.getTerm());
39+
node.addItem(JsonNodeFactory.createObjectIdNode(idAttribute(ctx), iri));
40+
return node;
41+
}
42+
}

src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/ObjectPropertyValueSerializer.java

-10
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,7 @@ public ObjectPropertyValueSerializer(ObjectGraphTraverser graphTraverser) {
3434

3535
@Override
3636
public JsonNode serialize(Object value, SerializationContext ctx) {
37-
if (value.getClass().isEnum()) {
38-
return serializeEnumConstant((Enum<?>) value, ctx);
39-
}
4037
graphTraverser.traverse(ctx);
4138
return null;
4239
}
43-
44-
private static JsonNode serializeEnumConstant(Enum<?> constant, SerializationContext<?> ctx) {
45-
final String iri = EnumUtil.resolveMappedIndividual(constant);
46-
final ObjectNode node = JsonNodeFactory.createObjectNode(ctx.getTerm());
47-
node.addItem(JsonNodeFactory.createObjectIdNode(JsonLd.ID, iri));
48-
return node;
49-
}
5040
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package cz.cvut.kbss.jsonld.serialization.serializer.context;
2+
3+
import cz.cvut.kbss.jsonld.ConfigParam;
4+
import cz.cvut.kbss.jsonld.Configuration;
5+
import cz.cvut.kbss.jsonld.common.BeanClassProcessor;
6+
import cz.cvut.kbss.jsonld.common.EnumUtil;
7+
import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory;
8+
import cz.cvut.kbss.jsonld.serialization.model.JsonNode;
9+
import cz.cvut.kbss.jsonld.serialization.serializer.compact.IndividualSerializer;
10+
import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext;
11+
12+
public class ContextBuildingIndividualSerializer extends IndividualSerializer {
13+
14+
private boolean serializeUsingExtendedDefinition;
15+
16+
@Override
17+
public JsonNode serialize(Object value, SerializationContext ctx) {
18+
if (serializeUsingExtendedDefinition) {
19+
if (BeanClassProcessor.isIdentifierType(value.getClass())) {
20+
return JsonNodeFactory.createStringLiteralNode(ctx.getTerm(), value.toString());
21+
} else {
22+
return JsonNodeFactory.createStringLiteralNode(ctx.getTerm(),
23+
EnumUtil.resolveMappedIndividual((Enum<?>) value));
24+
}
25+
}
26+
return super.serialize(value, ctx);
27+
}
28+
29+
@Override
30+
public void configure(Configuration config) {
31+
this.serializeUsingExtendedDefinition = config.is(ConfigParam.SERIALIZE_INDIVIDUALS_USING_EXPANDED_DEFINITION);
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,44 @@
11
package cz.cvut.kbss.jsonld.serialization.serializer.context;
22

3+
import cz.cvut.kbss.jsonld.ConfigParam;
4+
import cz.cvut.kbss.jsonld.Configuration;
5+
import cz.cvut.kbss.jsonld.JsonLd;
6+
import cz.cvut.kbss.jsonld.common.EnumUtil;
7+
import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory;
38
import cz.cvut.kbss.jsonld.serialization.model.JsonNode;
9+
import cz.cvut.kbss.jsonld.serialization.serializer.SerializerUtils;
410
import cz.cvut.kbss.jsonld.serialization.serializer.compact.ObjectPropertyValueSerializer;
511
import cz.cvut.kbss.jsonld.serialization.traversal.ObjectGraphTraverser;
612
import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext;
713

814
public class ContextBuildingObjectPropertyValueSerializer extends ObjectPropertyValueSerializer {
915

16+
private boolean serializeUsingExtendedDefinition;
17+
1018
public ContextBuildingObjectPropertyValueSerializer(ObjectGraphTraverser graphTraverser) {
1119
super(graphTraverser);
1220
}
1321

1422
@Override
1523
public JsonNode serialize(Object value, SerializationContext ctx) {
1624
if (ctx.getTerm() != null) {
17-
ctx.registerTermMapping(ctx.getFieldName(), ctx.getTerm());
25+
registerTermDefinition(ctx);
1826
}
1927
return super.serialize(value, ctx);
2028
}
29+
30+
private void registerTermDefinition(SerializationContext<?> ctx) {
31+
if (serializeUsingExtendedDefinition) {
32+
ctx.registerTermMapping(ctx.getFieldName(),
33+
SerializerUtils.createTypedTermDefinition(ctx.getFieldName(), ctx.getTerm(),
34+
JsonLd.ID));
35+
} else {
36+
ctx.registerTermMapping(ctx.getFieldName(), ctx.getTerm());
37+
}
38+
}
39+
40+
@Override
41+
public void configure(Configuration config) {
42+
this.serializeUsingExtendedDefinition = config.is(ConfigParam.SERIALIZE_INDIVIDUALS_USING_EXPANDED_DEFINITION);
43+
}
2144
}

src/main/java/cz/cvut/kbss/jsonld/serialization/traversal/InstanceVisitor.java

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919

2020
public interface InstanceVisitor {
2121

22+
/**
23+
* Visits an object that represents an individual, without any additional attributes.
24+
*
25+
* This can be an identifier-based attribute value (e.g., URI), or an enum constant mapped to an individual.
26+
* @param ctx Current serialization context
27+
*/
28+
void visitIndividual(SerializationContext<?> ctx);
29+
2230
/**
2331
* Visits the instance represented by the specified context.
2432
* <p>

src/main/java/cz/cvut/kbss/jsonld/serialization/traversal/ObjectGraphTraverser.java

+11-17
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import cz.cvut.kbss.jsonld.common.BeanAnnotationProcessor;
1616
import cz.cvut.kbss.jsonld.common.BeanClassProcessor;
17-
import cz.cvut.kbss.jsonld.common.EnumUtil;
1817
import cz.cvut.kbss.jsonld.common.IdentifierUtil;
1918
import cz.cvut.kbss.jsonld.exception.MissingIdentifierException;
2019

@@ -87,19 +86,22 @@ void traverseSingular(SerializationContext<?> ctx) {
8786
if (!shouldTraverse) {
8887
return;
8988
}
89+
if (isIndividual(ctx)) {
90+
visitor.visitIndividual(ctx);
91+
return;
92+
}
9093
openInstance(ctx);
9194
visitIdentifier(ctx);
92-
if (shouldTraverseObject(ctx, firstEncounter)) {
95+
if (firstEncounter) {
9396
visitTypes(ctx);
9497
serializeFields(ctx);
9598
serializePropertiesField(ctx);
9699
}
97100
closeInstance(ctx);
98101
}
99102

100-
private boolean shouldTraverseObject(SerializationContext<?> ctx, boolean firstEncounter) {
101-
return firstEncounter && !BeanClassProcessor.isIdentifierType(ctx.getValue().getClass()) &&
102-
!ctx.getValue().getClass().isEnum();
103+
private static boolean isIndividual(SerializationContext<?> ctx) {
104+
return BeanClassProcessor.isIdentifierType(ctx.getValue().getClass()) || ctx.getValue().getClass().isEnum();
103105
}
104106

105107
private void serializeFields(SerializationContext<?> ctx) {
@@ -185,18 +187,10 @@ public void visitIdentifier(SerializationContext<?> ctx) {
185187
final Class<?> idCls = identifier.getClass();
186188
final String id;
187189
final SerializationContext<String> idContext;
188-
if (BeanClassProcessor.isIdentifierType(idCls)) {
189-
id = identifier.toString();
190-
idContext = serializationContextFactory.createForIdentifier(null, id, ctx);
191-
} else if (idCls.isEnum()) {
192-
id = EnumUtil.resolveMappedIndividual((Enum<?>) identifier);
193-
idContext = serializationContextFactory.createForIdentifier(null, id, ctx);
194-
} else {
195-
id = resolveIdentifier(identifier);
196-
idContext = serializationContextFactory.createForIdentifier(
197-
BeanAnnotationProcessor.getIdentifierField(idCls).orElse(null), id, ctx);
198-
knownInstances.put(identifier, id);
199-
}
190+
id = resolveIdentifier(identifier);
191+
idContext = serializationContextFactory.createForIdentifier(
192+
BeanAnnotationProcessor.getIdentifierField(idCls).orElse(null), id, ctx);
193+
knownInstances.put(identifier, id);
200194
visitor.visitIdentifier(idContext);
201195
}
202196

0 commit comments

Comments
 (0)