diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f8908d..8f4434b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # JB4JSON-LD Changelog +## 0.15.1 - 2024-12-17 +- Ensure datatype is output with numeric values to preserve their type on serialization (Enhancement #66). +- Dependency updates: JOPA 2.2.1, build plugins. + ## 0.15.0 - 2024-08-26 - Support deserializing objects containing only identifier when `ASSUME_TARGET_TYPE` is enabled (Enhancement #69). - Cache deserialization type map (Enhancement #68). diff --git a/pom.xml b/pom.xml index e6ba437..18f0ade 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ cz.cvut.kbss.jsonld jb4jsonld - 0.15.0 + 0.15.1 JB4JSON-LD Java Binding for JSON-LD allows serialization and deserialization of Java POJOs to/from JSON-LD. This is the core implementation, which has to be integrated with Jackson, Jersey etc. @@ -19,12 +19,12 @@ ${java.version} ${java.version} - 2.0.4 + 2.2.1 5.11.0 5.12.0 1.5.7 - 5.0.2 + 5.0.3 @@ -128,7 +128,7 @@ maven-javadoc-plugin - 3.4.1 + 3.10.1 attach-javadocs @@ -172,19 +172,19 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.5.1 org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.12 diff --git a/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java b/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java index ea81de7..baca0c3 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java +++ b/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java @@ -46,6 +46,9 @@ import java.util.function.Function; import java.util.stream.Collectors; +/** + * Utilities for mapping-related tasks. + */ public class BeanAnnotationProcessor { private static final String[] EMPTY_ARRAY = new String[0]; diff --git a/src/main/java/cz/cvut/kbss/jsonld/common/BeanClassProcessor.java b/src/main/java/cz/cvut/kbss/jsonld/common/BeanClassProcessor.java index 445f6a1..f43048b 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/common/BeanClassProcessor.java +++ b/src/main/java/cz/cvut/kbss/jsonld/common/BeanClassProcessor.java @@ -27,6 +27,9 @@ import java.lang.reflect.Type; import java.util.*; +/** + * Reflection-related utilities. + */ public class BeanClassProcessor { private BeanClassProcessor() { @@ -82,7 +85,8 @@ public static void setFieldValue(Field field, Object instance, Object value) { public static T createInstance(Class cls) { try { return cls.getDeclaredConstructor().newInstance(); - } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { + } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | + IllegalAccessException e) { throw new BeanProcessingException("Class " + cls + " is missing a public no-arg constructor.", e); } } @@ -94,14 +98,10 @@ public static T createInstance(Class cls) { * @return New collection instance */ public static Collection createCollection(CollectionType type) { - switch (type) { - case LIST: - return new ArrayList<>(); - case SET: - return new HashSet<>(); - default: - throw new IllegalArgumentException("Unsupported collection type " + type); - } + return switch (type) { + case LIST -> new ArrayList<>(); + case SET -> new HashSet<>(); + }; } /** @@ -249,8 +249,9 @@ public static void verifyPropertiesFieldType(Field field) { /** * Checks whether the specified class represents an individual reference and not a complex object. - * + *

* Individual references are identifiers or enum constants mapped to individuals. + * * @param cls Class to check * @return {@code true} when the type represents an individual, {@code false} otherwise * @see #isIdentifierType(Class) diff --git a/src/main/java/cz/cvut/kbss/jsonld/deserialization/util/ClasspathScanner.java b/src/main/java/cz/cvut/kbss/jsonld/deserialization/util/ClasspathScanner.java index c716d34..1e172a5 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/deserialization/util/ClasspathScanner.java +++ b/src/main/java/cz/cvut/kbss/jsonld/deserialization/util/ClasspathScanner.java @@ -133,7 +133,7 @@ protected void processJarFile(URL jarResource, String packageName) { } } } catch (IOException e) { - LOG.error("Unable to scan classes in JAR file " + jarPath, e); + LOG.error("Unable to scan classes in JAR file {}", jarPath, e); } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializer.java index 82018a3..9ecbf44 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializer.java @@ -24,14 +24,21 @@ import cz.cvut.kbss.jsonld.serialization.serializer.LiteralValueSerializers; import cz.cvut.kbss.jsonld.serialization.serializer.ObjectGraphValueSerializers; import cz.cvut.kbss.jsonld.serialization.serializer.ValueSerializers; -import cz.cvut.kbss.jsonld.serialization.serializer.compact.*; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.DefaultValueSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.IdentifierSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.IndividualSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.MultilingualStringSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.NumberSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.ObjectPropertyValueSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.TypesSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.compact.datetime.TemporalAmountSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.compact.datetime.TemporalSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.datetime.DateSerializer; import cz.cvut.kbss.jsonld.serialization.traversal.ObjectGraphTraverser; import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContextFactory; -import java.time.*; +import java.time.Duration; +import java.time.Period; import java.util.Date; /** @@ -57,19 +64,14 @@ protected ValueSerializers initSerializers() { valueSerializers.registerTypesSerializer(new TypesSerializer()); valueSerializers.registerIndividualSerializer(new IndividualSerializer()); final TemporalSerializer ts = new TemporalSerializer(); - valueSerializers.registerSerializer(LocalDate.class, ts); // Register the same temporal serializer for each of the types it supports (needed for key-based map access) - valueSerializers.registerSerializer(LocalDate.class, ts); - valueSerializers.registerSerializer(LocalTime.class, ts); - valueSerializers.registerSerializer(OffsetTime.class, ts); - valueSerializers.registerSerializer(LocalDateTime.class, ts); - valueSerializers.registerSerializer(OffsetDateTime.class, ts); - valueSerializers.registerSerializer(ZonedDateTime.class, ts); - valueSerializers.registerSerializer(Instant.class, ts); + TemporalSerializer.getSupportedTypes().forEach(cls -> valueSerializers.registerSerializer(cls, ts)); valueSerializers.registerSerializer(Date.class, new DateSerializer(ts)); final TemporalAmountSerializer tas = new TemporalAmountSerializer(); valueSerializers.registerSerializer(Duration.class, tas); valueSerializers.registerSerializer(Period.class, tas); + final NumberSerializer numberSerializer = new NumberSerializer(); + NumberSerializer.getSupportedTypes().forEach(cls -> valueSerializers.registerSerializer(cls, numberSerializer)); return valueSerializers; } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializer.java index 2042c30..0cbed63 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializer.java @@ -33,14 +33,22 @@ import cz.cvut.kbss.jsonld.serialization.serializer.ObjectGraphValueSerializers; import cz.cvut.kbss.jsonld.serialization.serializer.ValueSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.ValueSerializers; -import cz.cvut.kbss.jsonld.serialization.serializer.context.*; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingDefaultValueSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingIdentifierSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingIndividualSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingMultilingualStringSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingNumberSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingObjectPropertyValueSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingPluralMultilingualStringSerializer; +import cz.cvut.kbss.jsonld.serialization.serializer.context.ContextBuildingTypesSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.context.datetime.ContextBuildingTemporalAmountSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.context.datetime.ContextBuildingTemporalSerializer; import cz.cvut.kbss.jsonld.serialization.serializer.datetime.DateSerializer; import cz.cvut.kbss.jsonld.serialization.traversal.ObjectGraphTraverser; import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContextFactory; -import java.time.*; +import java.time.Duration; +import java.time.Period; import java.util.Collection; import java.util.Date; import java.util.Optional; @@ -69,19 +77,16 @@ protected ValueSerializers initSerializers() { valueSerializers.registerTypesSerializer(new ContextBuildingTypesSerializer()); valueSerializers.registerIndividualSerializer(new ContextBuildingIndividualSerializer()); final ContextBuildingTemporalSerializer ts = new ContextBuildingTemporalSerializer(); - valueSerializers.registerSerializer(LocalDate.class, ts); // Register the same temporal serializer for each of the types it supports (needed for key-based map access) - valueSerializers.registerSerializer(LocalDate.class, ts); - valueSerializers.registerSerializer(LocalTime.class, ts); - valueSerializers.registerSerializer(OffsetTime.class, ts); - valueSerializers.registerSerializer(LocalDateTime.class, ts); - valueSerializers.registerSerializer(OffsetDateTime.class, ts); - valueSerializers.registerSerializer(ZonedDateTime.class, ts); - valueSerializers.registerSerializer(Instant.class, ts); + ContextBuildingTemporalSerializer.getSupportedTypes() + .forEach(cls -> valueSerializers.registerSerializer(cls, ts)); valueSerializers.registerSerializer(Date.class, new DateSerializer(ts)); final ContextBuildingTemporalAmountSerializer tas = new ContextBuildingTemporalAmountSerializer(); valueSerializers.registerSerializer(Duration.class, tas); valueSerializers.registerSerializer(Period.class, tas); + final ContextBuildingNumberSerializer ns = new ContextBuildingNumberSerializer(); + ContextBuildingNumberSerializer.getSupportedTypes() + .forEach(cls -> valueSerializers.registerSerializer(cls, ns)); return valueSerializers; } @@ -114,7 +119,8 @@ private void ensureContextNodeNotPresent(CompositeNode root, JsonNode rootCtx private JsonLdTreeBuilder initTreeBuilder(ObjectGraphTraverser traverser, JsonLdContextFactory jsonLdContextFactory) { - final ContextBuildingObjectPropertyValueSerializer opSerializer = new ContextBuildingObjectPropertyValueSerializer(traverser); + final ContextBuildingObjectPropertyValueSerializer opSerializer = + new ContextBuildingObjectPropertyValueSerializer(traverser); opSerializer.configure(configuration()); return new JsonLdTreeBuilder(new ObjectGraphValueSerializers(serializers, opSerializer), jsonLdContextFactory); } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/JsonNodeFactory.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/JsonNodeFactory.java index 7183c85..f7b4831 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/JsonNodeFactory.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/JsonNodeFactory.java @@ -43,14 +43,11 @@ public static LiteralNode createLiteralNode(Object value) { public static LiteralNode createLiteralNode(String name, Object value) { final LiteralType type = determineLiteralType(value); - switch (type) { - case BOOLEAN: - return createBooleanLiteralNode(name, (Boolean) value); - case NUMBER: - return createNumericLiteralNode(name, (Number) value); - default: - return createStringLiteralNode(name, value.toString()); - } + return switch (type) { + case BOOLEAN -> createBooleanLiteralNode(name, (Boolean) value); + case NUMBER -> createNumericLiteralNode(name, (Number) value); + default -> createStringLiteralNode(name, value.toString()); + }; } private static LiteralType determineLiteralType(Object value) { @@ -84,14 +81,10 @@ public static StringLiteralNode createStringLiteralNode(String name, String valu */ public static CollectionNode createCollectionNode(String name, Collection value) { final CollectionType type = determineCollectionType(value); - switch (type) { - case LIST: - return new ListNode(name); - case SET: - return createSetNode(name); - default: - throw new IllegalArgumentException("Unsupported collection type " + type); - } + return switch (type) { + case LIST -> new ListNode(name); + case SET -> createSetNode(name); + }; } private static CollectionType determineCollectionType(Collection collection) { diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/model/CollectionNode.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/model/CollectionNode.java index 075ec66..2a97456 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/model/CollectionNode.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/model/CollectionNode.java @@ -19,6 +19,11 @@ import java.util.Collection; +/** + * JSON node representing a collection. + * + * @param Node type + */ public abstract class CollectionNode> extends CompositeNode { CollectionNode() { diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/SerializerUtils.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/SerializerUtils.java index afdbf39..dd41b2a 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/SerializerUtils.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/SerializerUtils.java @@ -70,7 +70,7 @@ public static ObjectNode createTypedTermDefinition(String term, String id, Strin * @param type Value type to use * @return Resulting JSON node */ - public static JsonNode createdTypedValueNode(String term, String value, String type) { + public static JsonNode createdTypedValueNode(String term, Object value, String type) { final ObjectNode node = JsonNodeFactory.createObjectNode(term); node.addItem(JsonNodeFactory.createLiteralNode(JsonLd.TYPE, type)); node.addItem(JsonNodeFactory.createLiteralNode(JsonLd.VALUE, value)); diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/NumberSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/NumberSerializer.java new file mode 100644 index 0000000..a2e6c84 --- /dev/null +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/NumberSerializer.java @@ -0,0 +1,54 @@ +package cz.cvut.kbss.jsonld.serialization.serializer.compact; + +import cz.cvut.kbss.jopa.vocabulary.XSD; +import cz.cvut.kbss.jsonld.serialization.model.JsonNode; +import cz.cvut.kbss.jsonld.serialization.serializer.SerializerUtils; +import cz.cvut.kbss.jsonld.serialization.serializer.ValueSerializer; +import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; + +/** + * Serializes numeric values. + */ +public class NumberSerializer implements ValueSerializer { + + @Override + public JsonNode serialize(Number value, SerializationContext ctx) { + Objects.requireNonNull(value); + return SerializerUtils.createdTypedValueNode(ctx.getTerm(), value, getDatatype(value)); + } + + protected String getDatatype(Number value) { + if (value instanceof Integer) { + return XSD.INT; + } else if (value instanceof Long) { + return XSD.LONG; + } else if (value instanceof Double) { + return XSD.DOUBLE; + } else if (value instanceof Float) { + return XSD.FLOAT; + } else if (value instanceof Short) { + return XSD.SHORT; + } else if (value instanceof BigInteger) { + return XSD.INTEGER; + } else if (value instanceof BigDecimal) { + return XSD.DECIMAL; + } else { + throw new IllegalArgumentException("Unsupported numeric literal type " + value.getClass()); + } + } + + /** + * Gets a list of Java types supported by this serializer. + * + * @return List of Java classes + */ + public static List> getSupportedTypes() { + return List.of(Integer.class, Long.class, Double.class, Float.class, Short.class, BigInteger.class, + BigDecimal.class); + } +} diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/datetime/TemporalSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/datetime/TemporalSerializer.java index 46798f5..c98e127 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/datetime/TemporalSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/datetime/TemporalSerializer.java @@ -25,8 +25,15 @@ import cz.cvut.kbss.jsonld.serialization.serializer.datetime.DateTimeSerializer; import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext; -import java.time.*; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; import java.time.temporal.TemporalAccessor; +import java.util.List; /** * Serializes Java 8 date/time values represented by the {@link TemporalAccessor} interface. @@ -41,7 +48,8 @@ public TemporalSerializer() { this(new IsoDateTimeSerializer(), new LocalDateSerializer(), new TimeSerializer()); } - protected TemporalSerializer(DateTimeSerializer dateTimeSerializer, LocalDateSerializer dateSerializer, TimeSerializer timeSerializer) { + protected TemporalSerializer(DateTimeSerializer dateTimeSerializer, LocalDateSerializer dateSerializer, + TimeSerializer timeSerializer) { this.dateTimeSerializer = dateTimeSerializer; this.dateSerializer = dateSerializer; this.timeSerializer = timeSerializer; @@ -64,14 +72,25 @@ public JsonNode serialize(TemporalAccessor value, SerializationContext> getSupportedTypes() { + return List.of(LocalDate.class, OffsetTime.class, LocalTime.class, OffsetDateTime.class, LocalDateTime.class, + Instant.class, ZonedDateTime.class); + } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/ContextBuildingNumberSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/ContextBuildingNumberSerializer.java new file mode 100644 index 0000000..d145774 --- /dev/null +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/ContextBuildingNumberSerializer.java @@ -0,0 +1,26 @@ +package cz.cvut.kbss.jsonld.serialization.serializer.context; + +import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory; +import cz.cvut.kbss.jsonld.serialization.model.JsonNode; +import cz.cvut.kbss.jsonld.serialization.model.ObjectNode; +import cz.cvut.kbss.jsonld.serialization.serializer.SerializerUtils; +import cz.cvut.kbss.jsonld.serialization.serializer.compact.NumberSerializer; +import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext; + +/** + * Serializes numeric values and builds a corresponding context item for them. + */ +public class ContextBuildingNumberSerializer extends NumberSerializer { + + @Override + public JsonNode serialize(Number value, SerializationContext ctx) { + if (ctx.getTerm() != null && ctx.getFieldName() != null) { + final ObjectNode termDef = + SerializerUtils.createTypedTermDefinition(ctx.getFieldName(), ctx.getTerm(), getDatatype(value)); + ctx.registerTermMapping(ctx.getFieldName(), termDef); + return JsonNodeFactory.createLiteralNode(ctx.getTerm(), value); + } else { + return super.serialize(value, ctx); + } + } +} diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingEpochBasedDateTimeSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingEpochBasedDateTimeSerializer.java index 3b76f95..8c976b0 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingEpochBasedDateTimeSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingEpochBasedDateTimeSerializer.java @@ -28,7 +28,7 @@ public class ContextBuildingEpochBasedDateTimeSerializer extends EpochBasedDateT @Override public JsonNode serialize(OffsetDateTime value, SerializationContext ctx) { - if (ctx.getTerm() != null) { + if (ctx.getTerm() != null && ctx.getFieldName() != null) { ctx.registerTermMapping(ctx.getFieldName(), ctx.getTerm()); } return super.serialize(value, ctx); diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingIsoDateTimeSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingIsoDateTimeSerializer.java index 4b11f3a..492abf5 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingIsoDateTimeSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingIsoDateTimeSerializer.java @@ -32,11 +32,13 @@ public class ContextBuildingIsoDateTimeSerializer extends IsoDateTimeSerializer @Override public JsonNode serialize(OffsetDateTime value, SerializationContext ctx) { - if (ctx.getTerm() != null) { + if (ctx.getTerm() != null && ctx.getFieldName() != null) { final ObjectNode termDef = SerializerUtils.createTypedTermDefinition(ctx.getFieldName(), ctx.getTerm(), XSD.DATETIME); ctx.registerTermMapping(ctx.getFieldName(), termDef); + return JsonNodeFactory.createLiteralNode(ctx.getTerm(), formatter.format(value)); + } else { + return super.serialize(value, ctx); } - return JsonNodeFactory.createLiteralNode(ctx.getTerm(), formatter.format(value)); } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingLocalDateSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingLocalDateSerializer.java index 5679beb..b07b976 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingLocalDateSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingLocalDateSerializer.java @@ -32,11 +32,13 @@ public class ContextBuildingLocalDateSerializer extends LocalDateSerializer { @Override public JsonNode serialize(LocalDate value, SerializationContext ctx) { - if (ctx.getTerm() != null) { + if (ctx.getTerm() != null && ctx.getFieldName() != null) { final ObjectNode termDef = SerializerUtils.createTypedTermDefinition(ctx.getFieldName(), ctx.getTerm(), XSD.DATE); ctx.registerTermMapping(ctx.getFieldName(), termDef); + return JsonNodeFactory.createLiteralNode(ctx.getTerm(), FORMATTER.format(value)); + } else { + return super.serialize(value, ctx); } - return JsonNodeFactory.createLiteralNode(ctx.getTerm(), FORMATTER.format(value)); } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTemporalAmountSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTemporalAmountSerializer.java index 3688a56..b0f1d90 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTemporalAmountSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTemporalAmountSerializer.java @@ -31,11 +31,13 @@ public class ContextBuildingTemporalAmountSerializer implements ValueSerializer< @Override public JsonNode serialize(TemporalAmount value, SerializationContext ctx) { - if (ctx.getTerm() != null) { + if (ctx.getTerm() != null && ctx.getFieldName() != null) { final ObjectNode termDef = SerializerUtils.createTypedTermDefinition(ctx.getFieldName(), ctx.getTerm(), XSD.DURATION); ctx.registerTermMapping(ctx.getFieldName(), termDef); + return JsonNodeFactory.createLiteralNode(ctx.getTerm(), value.toString()); + } else { + return SerializerUtils.createdTypedValueNode(ctx.getTerm(), value, XSD.DURATION); } - return JsonNodeFactory.createLiteralNode(ctx.getTerm(), value.toString()); } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTimeSerializer.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTimeSerializer.java index c042177..1a0f52e 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTimeSerializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/serializer/context/datetime/ContextBuildingTimeSerializer.java @@ -31,11 +31,13 @@ public class ContextBuildingTimeSerializer extends cz.cvut.kbss.jsonld.serializa @Override public JsonNode serialize(OffsetTime value, SerializationContext ctx) { - if (ctx.getTerm() != null) { + if (ctx.getTerm() != null && ctx.getFieldName() != null) { final ObjectNode termDef = SerializerUtils.createTypedTermDefinition(ctx.getFieldName(), ctx.getTerm(), XSD.TIME); ctx.registerTermMapping(ctx.getFieldName(), termDef); + return JsonNodeFactory.createLiteralNode(ctx.getTerm(), FORMATTER.format(value)); + } else { + return super.serialize(value, ctx); } - return JsonNodeFactory.createLiteralNode(ctx.getTerm(), FORMATTER.format(value)); } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/serialization/traversal/PropertiesTraverser.java b/src/main/java/cz/cvut/kbss/jsonld/serialization/traversal/PropertiesTraverser.java index 2f24d06..24ec1a8 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/serialization/traversal/PropertiesTraverser.java +++ b/src/main/java/cz/cvut/kbss/jsonld/serialization/traversal/PropertiesTraverser.java @@ -45,8 +45,7 @@ public void traverseProperties(SerializationContext> ctx) { if (e.getValue() == null) { continue; } - if (e.getValue() instanceof Collection) { - final Collection propertyValues = (Collection) e.getValue(); + if (e.getValue() instanceof Collection propertyValues) { serializePropertyValues(property, propertyValues, ctx.getJsonLdContext()); } else { visitSingleValue(property, e.getValue(), ctx.getJsonLdContext()); diff --git a/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java index 6db0a31..939dbb4 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java @@ -36,6 +36,7 @@ import cz.cvut.kbss.jsonld.environment.model.GenericMember; import cz.cvut.kbss.jsonld.environment.model.ObjectWithAnnotationProperties; import cz.cvut.kbss.jsonld.environment.model.ObjectWithMultilingualString; +import cz.cvut.kbss.jsonld.environment.model.ObjectWithNumericAttributes; import cz.cvut.kbss.jsonld.environment.model.ObjectWithPluralMultilingualString; import cz.cvut.kbss.jsonld.environment.model.Organization; import cz.cvut.kbss.jsonld.environment.model.OwlPropertyType; @@ -56,6 +57,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URI; import java.util.Date; import java.util.HashMap; @@ -106,7 +109,7 @@ private static Map initUsers() { @BeforeEach void setUp() { final Configuration config = new Configuration(); - config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); + config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = JsonLdDeserializer.createExpandedDeserializer(config); } @@ -340,9 +343,6 @@ void deserializationSupportsObjectsWithBlankNodeIds() throws Exception { @Test void deserializationReturnsSubclassInstanceWhenTypesMatch() throws Exception { - final Configuration config = new Configuration(); - config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); - this.sut = JsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithDataProperties.json"); final Person result = sut.deserialize(input, Person.class); assertInstanceOf(User.class, result); @@ -353,6 +353,7 @@ void deserializationSupportsPolymorphismForCollections() throws Exception { final Configuration config = new Configuration(); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = JsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithPluralReference.json"); final PolymorphicOrganization result = sut.deserialize(input, PolymorphicOrganization.class); @@ -379,6 +380,7 @@ void deserializationSupportsPolymorphismForAttributes() throws Exception { final Configuration config = new Configuration(); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = JsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithSingularPolymorphicReference.json"); final PolymorphicPerson result = sut.deserialize(input, PolymorphicPerson.class); @@ -401,6 +403,7 @@ public PolymorphicPerson() { void deserializationSupportsDeserializingObjectAsPlainIdentifier() throws Exception { final Configuration config = new Configuration(); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); this.sut = JsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithSingularPolymorphicReference.json"); @@ -426,6 +429,7 @@ void deserializationSupportsDeserializingObjectsAsPluralPlainIdentifiers() throw final Configuration config = new Configuration(); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = JsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithPluralReference.json"); final OrganizationWithPlainIdentifiers result = @@ -454,6 +458,7 @@ void deserializationHandlesJsonLdListOfValues() throws Exception { final Configuration config = new Configuration(); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = JsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithList.json"); final OrganizationWithListOfMembers result = @@ -589,10 +594,10 @@ void deserializationHandlesTypedDataPropertyValues() throws Exception { void deserializationHandlesObjectPropertyFieldOfTypeObject() throws Exception { final JsonArray input = readAndExpand("objectWithSingularReference.json"); final Configuration config = new Configuration(); - config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.TRUE.toString()); // This will prevent problems with multiple classes matching the same type (Organization) config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final GenericMember result = sut.deserialize(input, GenericMember.class); assertNotNull(result.getMemberOf()); @@ -643,6 +648,7 @@ void deserializationSupportsPlainObjectReturnType() throws Exception { config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.TRUE.toString()); // This will prevent problems with multiple classes matching the same type (Organization) config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithDataProperties.json"); final Object result = sut.deserialize(input, Object.class); @@ -655,6 +661,7 @@ void deserializationUsesProvidedTargetTypeWhenNoTypeIsSpecifiedAndTypeAssumingIs final Configuration config = new Configuration(); config.set(ConfigParam.ASSUME_TARGET_TYPE, Boolean.TRUE.toString()); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithoutTypes.json"); final User result = sut.deserialize(input, User.class); @@ -724,6 +731,7 @@ void deserializationSupportsOptimisticTargetTypeResolution() throws Exception { final Configuration config = sut.configuration(); config.set(ConfigParam.ENABLE_OPTIMISTIC_TARGET_TYPE_RESOLUTION, Boolean.toString(true)); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithPluralOptimisticallyTypedReference.json"); final StudyOnPersons result = sut.deserialize(input, StudyOnPersons.class); @@ -738,6 +746,7 @@ void deserializationSupportsOptimisticTargetTypeResolutionWithSuperclassPreferen config.set(ConfigParam.PREFER_SUPERCLASS, Boolean.toString(true)); config.set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithPluralOptimisticallyTypedReference.json"); final StudyOnPersons result = sut.deserialize(input, StudyOnPersons.class); @@ -748,6 +757,10 @@ void deserializationSupportsOptimisticTargetTypeResolutionWithSuperclassPreferen @Test void deserializationThrowsAmbiguousTargetTypeExceptionForAmbiguousTargetTypeWithDisabledOptimisticTargetTypeResolution() throws Exception { + final Configuration config = sut.configuration(); + config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); + this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithPluralOptimisticallyTypedReference.json"); assertThrows(AmbiguousTargetTypeException.class, () -> sut.deserialize(input, StudyOnPersons.class)); } @@ -854,6 +867,7 @@ void deserializationUsesProvidedTargetTypeWhenNoTypeIsSpecifiedTypeAssumingIsEna final Configuration config = new Configuration(); config.set(ConfigParam.ASSUME_TARGET_TYPE, Boolean.TRUE.toString()); config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld"); + config.set(ConfigParam.DISABLE_TYPE_MAP_CACHE, "true"); this.sut = ExpandedJsonLdDeserializer.createExpandedDeserializer(config); final JsonArray input = readAndExpand("objectWithSingularReferenceWithIdOnly.json"); final Employee result = sut.deserialize(input, Employee.class); @@ -863,4 +877,18 @@ void deserializationUsesProvidedTargetTypeWhenNoTypeIsSpecifiedTypeAssumingIsEna assertEquals(TestUtil.UNSC_URI, result.getEmployer().getUri()); assertNull(result.getEmployer().getName()); } + + @Test + void deserializationHandlesNumericValuesWithDatatypes() throws Exception { + final JsonArray input = readAndExpand("objectWithNumericValues.json"); + final ObjectWithNumericAttributes result = sut.deserialize(input, ObjectWithNumericAttributes.class); + assertNotNull(result); + assertEquals((short) 128, result.getShortValue()); + assertEquals(128, result.getIntValue()); + assertEquals(128L, result.getLongValue()); + assertEquals(128.3f, result.getFloatValue()); + assertEquals(128.3, result.getDoubleValue()); + assertEquals(BigInteger.valueOf(128000), result.getBigIntegerValue()); + assertEquals(BigDecimal.valueOf(128000.821), result.getBigDecimalValue()); + } } diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/GenericMember.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/GenericMember.java index 9a7dda0..ff93fc0 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/environment/model/GenericMember.java +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/GenericMember.java @@ -62,8 +62,7 @@ public void toRdf(Model model, ValueFactory vf, Set visited) { visited.add(uri); final IRI id = vf.createIRI(uri.toString()); model.add(id, RDF.TYPE, vf.createIRI(Vocabulary.GENERIC_MEMBER)); - if (memberOf != null && memberOf instanceof GeneratesRdf) { - final GeneratesRdf org = (GeneratesRdf) memberOf; + if (memberOf != null && memberOf instanceof GeneratesRdf org) { model.add(id, vf.createIRI(Vocabulary.IS_MEMBER_OF), vf.createIRI(org.getUri().toString())); org.toRdf(model, vf, visited); } diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/ObjectWithNumericAttributes.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/ObjectWithNumericAttributes.java new file mode 100644 index 0000000..c512ea0 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/ObjectWithNumericAttributes.java @@ -0,0 +1,144 @@ +package cz.cvut.kbss.jsonld.environment.model; + +import cz.cvut.kbss.jopa.model.annotations.Id; +import cz.cvut.kbss.jopa.model.annotations.OWLClass; +import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty; +import cz.cvut.kbss.jsonld.environment.Vocabulary; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.vocabulary.RDF; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.util.Set; + +@OWLClass(iri = Vocabulary.DEFAULT_PREFIX + "ObjectWithNumericAttributes") +public class ObjectWithNumericAttributes implements GeneratesRdf { + + @Id + private URI uri; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "shortValue") + private Short shortValue; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "intValue") + private Integer intValue; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "longValue") + private Long longValue; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "floatValue") + private Float floatValue; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "doubleValue") + private Double doubleValue; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "bigIntegerValue") + private BigInteger bigIntegerValue; + + @OWLDataProperty(iri = Vocabulary.DEFAULT_PREFIX + "bigDecimalValue") + private BigDecimal bigDecimalValue; + + public ObjectWithNumericAttributes() { + } + + public ObjectWithNumericAttributes(URI uri) { + this.uri = uri; + } + + @Override + public URI getUri() { + return uri; + } + + public void setUri(URI uri) { + this.uri = uri; + } + + public Short getShortValue() { + return shortValue; + } + + public void setShortValue(Short shortValue) { + this.shortValue = shortValue; + } + + public Integer getIntValue() { + return intValue; + } + + public void setIntValue(Integer intValue) { + this.intValue = intValue; + } + + public Long getLongValue() { + return longValue; + } + + public void setLongValue(Long longValue) { + this.longValue = longValue; + } + + public Float getFloatValue() { + return floatValue; + } + + public void setFloatValue(Float floatValue) { + this.floatValue = floatValue; + } + + public Double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(Double doubleValue) { + this.doubleValue = doubleValue; + } + + public BigInteger getBigIntegerValue() { + return bigIntegerValue; + } + + public void setBigIntegerValue(BigInteger bigIntegerValue) { + this.bigIntegerValue = bigIntegerValue; + } + + public BigDecimal getBigDecimalValue() { + return bigDecimalValue; + } + + public void setBigDecimalValue(BigDecimal bigDecimalValue) { + this.bigDecimalValue = bigDecimalValue; + } + + @Override + public void toRdf(Model model, ValueFactory vf, Set visited) { + final IRI subject = vf.createIRI(uri.toString()); + model.add(subject, RDF.TYPE, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "ObjectWithNumericAttributes")); + if (shortValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "shortValue"), vf.createLiteral(shortValue)); + } + if (intValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "intValue"), vf.createLiteral(intValue)); + } + if (longValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "longValue"), vf.createLiteral(longValue)); + } + if (floatValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "floatValue"), vf.createLiteral(floatValue)); + } + if (doubleValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "doubleValue"), vf.createLiteral(doubleValue)); + } + if (bigIntegerValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "bigIntegerValue"), + vf.createLiteral(bigIntegerValue)); + } + if (bigDecimalValue != null) { + model.add(subject, vf.createIRI(Vocabulary.DEFAULT_PREFIX + "bigDecimalValue"), + vf.createLiteral(bigDecimalValue)); + } + } +} diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/PersonWithTypedProperties.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/PersonWithTypedProperties.java index 8c8b9d0..836d162 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/environment/model/PersonWithTypedProperties.java +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/PersonWithTypedProperties.java @@ -87,14 +87,13 @@ public void toRdf(Model model, ValueFactory vf, Set visited) { model.add(id, vf.createIRI(Vocabulary.LAST_NAME), vf.createLiteral(lastName)); if (properties != null) { properties.forEach((k, v) -> v.forEach(o -> { - if (o instanceof GeneratesRdf) { - final GeneratesRdf entity = (GeneratesRdf) o; + if (o instanceof GeneratesRdf entity) { model.add(id, vf.createIRI(k.toString()), vf.createIRI(entity.getUri().toString())); entity.toRdf(model, vf, visited); } else if (o instanceof URI) { model.add(id, vf.createIRI(k.toString()), vf.createIRI(o.toString())); } else if (o instanceof Integer) { - model.add(id, vf.createIRI(k.toString()), vf.createLiteral(o.toString(), vf.createIRI(XSD.INTEGER))); + model.add(id, vf.createIRI(k.toString()), vf.createLiteral(o.toString(), vf.createIRI(XSD.INT))); } else { model.add(id, vf.createIRI(k.toString()), vf.createLiteral(o.toString())); } diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/Study.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/Study.java index 0436f7a..f03bfea 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/environment/model/Study.java +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/Study.java @@ -108,7 +108,7 @@ public void toRdf(Model model, ValueFactory vf, Set visited) { model.add(id, RDF.TYPE, vf.createIRI(Vocabulary.STUDY)); model.add(id, vf.createIRI(RDFS.LABEL), vf.createLiteral(name)); model.add(id, vf.createIRI(Vocabulary.NUMBER_OF_PEOPLE_INVOLVED), - vf.createLiteral(noOfPeopleInvolved.toString(), vf.createIRI(XSD.INTEGER))); + vf.createLiteral(noOfPeopleInvolved.toString(), vf.createIRI(XSD.INT))); if (members != null) { members.forEach(m -> { model.add(id, vf.createIRI(Vocabulary.HAS_MEMBER), vf.createIRI(m.getUri().toString())); diff --git a/src/test/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializerTest.java index 036f331..b71d5e7 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializerTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/serialization/ContextBuildingJsonLdSerializerTest.java @@ -36,6 +36,7 @@ import cz.cvut.kbss.jsonld.environment.model.Employee; import cz.cvut.kbss.jsonld.environment.model.ObjectWithAnnotationProperties; import cz.cvut.kbss.jsonld.environment.model.ObjectWithMultilingualString; +import cz.cvut.kbss.jsonld.environment.model.ObjectWithNumericAttributes; import cz.cvut.kbss.jsonld.environment.model.Organization; import cz.cvut.kbss.jsonld.environment.model.OwlPropertyType; import cz.cvut.kbss.jsonld.environment.model.Person; @@ -52,6 +53,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URI; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; @@ -409,4 +412,36 @@ void serializationAddsToContextMappingForCollectionValuedAnnotationPropertyAttri final JsonObject context = json.getJsonObject(JsonLd.CONTEXT); assertThat(context, hasKey("origins")); } + + @Test + @Override + void serializationIncludesDatatypeOfNumericLiterals() { + final ObjectWithNumericAttributes instance = new ObjectWithNumericAttributes(Generator.generateUri()); + instance.setDoubleValue(155.15); + instance.setFloatValue(155.15f); + instance.setLongValue(155L); + instance.setShortValue((short) 155); + instance.setIntValue(155); + instance.setBigIntegerValue(BigInteger.valueOf(155L)); + instance.setBigDecimalValue(BigDecimal.valueOf(155.15)); + + final JsonObject json = serializeAndRead(instance).asJsonObject(); + assertThat(json, hasKey(JsonLd.CONTEXT)); + assertEquals(JsonValue.ValueType.OBJECT, json.get(JsonLd.CONTEXT).getValueType()); + final JsonObject context = json.getJsonObject(JsonLd.CONTEXT); + checkContextTerm(context, "doubleValue", Vocabulary.DEFAULT_PREFIX + "doubleValue", XSD.DOUBLE); + checkContextTerm(context, "floatValue", Vocabulary.DEFAULT_PREFIX + "floatValue", XSD.FLOAT); + checkContextTerm(context, "longValue", Vocabulary.DEFAULT_PREFIX + "longValue", XSD.LONG); + checkContextTerm(context, "shortValue", Vocabulary.DEFAULT_PREFIX + "shortValue", XSD.SHORT); + checkContextTerm(context, "intValue", Vocabulary.DEFAULT_PREFIX + "intValue", XSD.INT); + checkContextTerm(context, "bigIntegerValue", Vocabulary.DEFAULT_PREFIX + "bigIntegerValue", XSD.INTEGER); + checkContextTerm(context, "bigDecimalValue", Vocabulary.DEFAULT_PREFIX + "bigDecimalValue", XSD.DECIMAL); + } + + private static void checkContextTerm(JsonObject context, String att, String iri, String datatype) { + assertThat(context, hasKey(att)); + final JsonObject keyCtx = context.getJsonObject(att); + assertEquals(datatype, keyCtx.getString(JsonLd.TYPE)); + assertEquals(iri, keyCtx.getString(JsonLd.ID)); + } } \ No newline at end of file diff --git a/src/test/java/cz/cvut/kbss/jsonld/serialization/JsonLdSerializerTestBase.java b/src/test/java/cz/cvut/kbss/jsonld/serialization/JsonLdSerializerTestBase.java index d5c07c8..f2a2aba 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/serialization/JsonLdSerializerTestBase.java +++ b/src/test/java/cz/cvut/kbss/jsonld/serialization/JsonLdSerializerTestBase.java @@ -20,6 +20,7 @@ import cz.cvut.kbss.jopa.model.annotations.Id; import cz.cvut.kbss.jopa.model.annotations.OWLClass; import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty; +import cz.cvut.kbss.jopa.vocabulary.XSD; import cz.cvut.kbss.jsonld.ConfigParam; import cz.cvut.kbss.jsonld.JsonLd; import cz.cvut.kbss.jsonld.common.IdentifierUtil; @@ -31,6 +32,7 @@ import cz.cvut.kbss.jsonld.environment.model.GeneratesRdf; import cz.cvut.kbss.jsonld.environment.model.GenericMember; import cz.cvut.kbss.jsonld.environment.model.ObjectWithAnnotationProperties; +import cz.cvut.kbss.jsonld.environment.model.ObjectWithNumericAttributes; import cz.cvut.kbss.jsonld.environment.model.Organization; import cz.cvut.kbss.jsonld.environment.model.OwlPropertyType; import cz.cvut.kbss.jsonld.environment.model.Person; @@ -43,6 +45,8 @@ import cz.cvut.kbss.jsonld.serialization.serializer.ValueSerializer; import cz.cvut.kbss.jsonld.serialization.util.BufferedJsonGenerator; import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; import jakarta.json.JsonValue; import org.eclipse.rdf4j.model.BNode; import org.eclipse.rdf4j.model.Model; @@ -59,6 +63,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.LocalDate; @@ -73,6 +79,7 @@ import java.util.stream.IntStream; import static cz.cvut.kbss.jsonld.environment.IsIsomorphic.isIsomorphic; +import static cz.cvut.kbss.jsonld.environment.TestUtil.parseAndExpand; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; @@ -473,4 +480,33 @@ void serializationSerializesAttributeWithCollectionOfEnumConstantsMappedToIndivi final Model actual = readJson(jsonWriter.getResult()); assertThat(actual, isIsomorphic(expected)); } + + @Test + void serializationIncludesDatatypeOfNumericLiterals() throws Exception { + final ObjectWithNumericAttributes instance = new ObjectWithNumericAttributes(Generator.generateUri()); + instance.setDoubleValue(155.15); + instance.setFloatValue(155.15f); + instance.setLongValue(155L); + instance.setShortValue((short) 155); + instance.setIntValue(155); + instance.setBigIntegerValue(BigInteger.valueOf(155L)); + instance.setBigDecimalValue(BigDecimal.valueOf(155.15)); + sut.serialize(instance); + final JsonArray result = parseAndExpand(jsonWriter.getResult()); + final JsonObject obj = result.getJsonObject(0); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "doubleValue", XSD.DOUBLE, instance.getDoubleValue()); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "floatValue", XSD.FLOAT, instance.getFloatValue()); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "longValue", XSD.LONG, instance.getLongValue()); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "shortValue", XSD.SHORT, instance.getShortValue()); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "intValue", XSD.INT, instance.getIntValue()); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "bigIntegerValue", XSD.INTEGER, instance.getBigIntegerValue()); + checkValueDatatype(obj, Vocabulary.DEFAULT_PREFIX + "bigDecimalValue", XSD.DECIMAL, instance.getBigDecimalValue()); + } + + private static void checkValueDatatype(JsonObject result, String attIri, String datatype, Number value) { + final JsonArray att = result.getJsonArray(attIri); + assertEquals(1, att.size()); + assertEquals(datatype, att.getJsonObject(0).getString("@type")); + assertEquals(value.toString(), att.getJsonObject(0).getJsonNumber("@value").toString()); + } } diff --git a/src/test/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/NumberSerializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/NumberSerializerTest.java new file mode 100644 index 0000000..623c1e4 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/jsonld/serialization/serializer/compact/NumberSerializerTest.java @@ -0,0 +1,48 @@ +package cz.cvut.kbss.jsonld.serialization.serializer.compact; + +import cz.cvut.kbss.jopa.vocabulary.XSD; +import cz.cvut.kbss.jsonld.JsonLd; +import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory; +import cz.cvut.kbss.jsonld.serialization.context.DummyJsonLdContext; +import cz.cvut.kbss.jsonld.serialization.model.JsonNode; +import cz.cvut.kbss.jsonld.serialization.model.ObjectNode; +import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class NumberSerializerTest { + + private final NumberSerializer sut = new NumberSerializer(); + + @ParameterizedTest + @MethodSource("serializationData") + void serializeSerializesNumberAsTypedValueNode(Number value, JsonNode expected) { + assertEquals(expected, sut.serialize(value, new SerializationContext<>(value, DummyJsonLdContext.INSTANCE))); + } + + static Stream serializationData() { + return Stream.of( + Arguments.of((short) 1, typedNode((short) 1, XSD.SHORT)), + Arguments.of(1, typedNode(1, XSD.INT)), + Arguments.of(1L, typedNode(1L, XSD.LONG)), + Arguments.of(1.0f, typedNode(1.0f, XSD.FLOAT)), + Arguments.of(1.0, typedNode(1.0, XSD.DOUBLE)), + Arguments.of(BigInteger.valueOf(1), typedNode(BigInteger.valueOf(1), XSD.INTEGER)), + Arguments.of(BigDecimal.valueOf(1.1), typedNode(BigDecimal.valueOf(1.1), XSD.DECIMAL)) + ); + } + + private static JsonNode typedNode(Number value, String datatype) { + final ObjectNode node = JsonNodeFactory.createObjectNode(); + node.addItem(JsonNodeFactory.createLiteralNode(JsonLd.TYPE, datatype)); + node.addItem(JsonNodeFactory.createLiteralNode(JsonLd.VALUE, value)); + return node; + } +} \ No newline at end of file diff --git a/src/test/java/cz/cvut/kbss/jsonld/serialization/serializer/context/ContextBuildingNumberSerializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/serialization/serializer/context/ContextBuildingNumberSerializerTest.java new file mode 100644 index 0000000..16381f0 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/jsonld/serialization/serializer/context/ContextBuildingNumberSerializerTest.java @@ -0,0 +1,62 @@ +package cz.cvut.kbss.jsonld.serialization.serializer.context; + +import cz.cvut.kbss.jopa.vocabulary.XSD; +import cz.cvut.kbss.jsonld.JsonLd; +import cz.cvut.kbss.jsonld.serialization.JsonNodeFactory; +import cz.cvut.kbss.jsonld.serialization.context.JsonLdContext; +import cz.cvut.kbss.jsonld.serialization.context.MappingJsonLdContext; +import cz.cvut.kbss.jsonld.serialization.model.JsonNode; +import cz.cvut.kbss.jsonld.serialization.model.ObjectNode; +import cz.cvut.kbss.jsonld.serialization.traversal.SerializationContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItems; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ContextBuildingNumberSerializerTest { + + private final ContextBuildingNumberSerializer sut = new ContextBuildingNumberSerializer(); + + @ParameterizedTest + @MethodSource("serializationData") + void serializeSerializesNumberAsTypedValueNode(Number value, String expectedDatatype) throws Exception { + final JsonLdContext ctx = new MappingJsonLdContext(); + sut.serialize(value, + new SerializationContext<>("http://example.com/number", + TestEntity.class.getDeclaredField("numberField"), value, + ctx)); + final Optional mapping = ctx.getTermMapping("numberField"); + assertTrue(mapping.isPresent()); + assertInstanceOf(ObjectNode.class, mapping.get()); + final ObjectNode objectNode = (ObjectNode) mapping.get(); + assertThat(objectNode.getItems(), + hasItems(JsonNodeFactory.createLiteralNode(JsonLd.ID, "http://example.com/number"), + JsonNodeFactory.createLiteralNode(JsonLd.TYPE, expectedDatatype))); + } + + static Stream serializationData() { + return Stream.of( + Arguments.of((short) 1, XSD.SHORT), + Arguments.of(1, XSD.INT), + Arguments.of(1L, XSD.LONG), + Arguments.of(1.0f, XSD.FLOAT), + Arguments.of(1.0, XSD.DOUBLE), + Arguments.of(BigInteger.valueOf(1), XSD.INTEGER), + Arguments.of(BigDecimal.valueOf(1.1), XSD.DECIMAL) + ); + } + + private static class TestEntity { + + private Number numberField; + } +} \ No newline at end of file diff --git a/src/test/resources/objectWithNumericValues.json b/src/test/resources/objectWithNumericValues.json new file mode 100644 index 0000000..4ad69ce --- /dev/null +++ b/src/test/resources/objectWithNumericValues.json @@ -0,0 +1,41 @@ +{ + "@context": { + "intValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/intValue", + "@type": "http://www.w3.org/2001/XMLSchema#int" + }, + "shortValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/shortValue", + "@type": "http://www.w3.org/2001/XMLSchema#short" + }, + "longValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/longValue", + "@type": "http://www.w3.org/2001/XMLSchema#long" + }, + "floatValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/floatValue", + "@type": "http://www.w3.org/2001/XMLSchema#float" + }, + "doubleValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/doubleValue", + "@type": "http://www.w3.org/2001/XMLSchema#double" + }, + "bigIntegerValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/bigIntegerValue", + "@type": "http://www.w3.org/2001/XMLSchema#integer" + }, + "bigDecimalValue": { + "@id": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/bigDecimalValue", + "@type": "http://www.w3.org/2001/XMLSchema#decimal" + } + }, + "@id": "http://example.com/objectWithNumericValues", + "@type": "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/ObjectWithNumericAttributes", + "shortValue": 128, + "intValue": 128, + "longValue": 128, + "floatValue": 128.3, + "doubleValue": 128.3, + "bigIntegerValue": 128000, + "bigDecimalValue": 128000.821 +} \ No newline at end of file