Skip to content

Commit a4aa36e

Browse files
authored
Merge pull request #72 from kbss-cvut/development
[0.15.0] Release
2 parents df11fb7 + 3926c4d commit a4aa36e

18 files changed

+374
-70
lines changed

.github/workflows/maven.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ jobs:
1212
runs-on: ubuntu-latest
1313

1414
steps:
15-
- uses: actions/checkout@v2
16-
- name: Set up JDK 11
17-
uses: actions/setup-java@v2
15+
- uses: actions/checkout@v4
16+
- name: Set up JDK 17
17+
uses: actions/setup-java@v3
1818
with:
1919
distribution: 'temurin'
20-
java-version: '11'
20+
java-version: '17'
2121
- name: Build with Maven
2222
run: mvn -B package --file pom.xml

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# JB4JSON-LD Changelog
22

3+
## 0.15.0 - 2024-08-26
4+
- Support deserializing objects containing only identifier when `ASSUME_TARGET_TYPE` is enabled (Enhancement #69).
5+
- Cache deserialization type map (Enhancement #68).
6+
- Dependency updates: JOPA 2.0.4, test deps.
7+
- **Breaking change:** Set Java 17 as minimum Java version.
8+
39
## 0.14.3 - 2024-04-16
410
- Fix missing context entry when serializing empty collection mapped to data/annotation property.
511

pom.xml

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>cz.cvut.kbss.jsonld</groupId>
88
<artifactId>jb4jsonld</artifactId>
9-
<version>0.14.3</version>
9+
<version>0.15.0</version>
1010
<name>JB4JSON-LD</name>
1111
<description>Java Binding for JSON-LD allows serialization and deserialization of Java POJOs to/from JSON-LD.
1212
This is the core implementation, which has to be integrated with Jackson, Jersey etc.
@@ -15,15 +15,16 @@
1515

1616
<properties>
1717
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18-
<maven.compiler.source>11</maven.compiler.source>
19-
<maven.compiler.target>11</maven.compiler.target>
18+
<java.version>17</java.version>
19+
<maven.compiler.source>${java.version}</maven.compiler.source>
20+
<maven.compiler.target>${java.version}</maven.compiler.target>
2021

21-
<cz.cvut.kbss.jopa.version>1.2.2</cz.cvut.kbss.jopa.version>
22+
<cz.cvut.kbss.jopa.version>2.0.4</cz.cvut.kbss.jopa.version>
2223

23-
<org.junit.jupiter.version>5.9.2</org.junit.jupiter.version>
24-
<org.mockito.version>4.11.0</org.mockito.version>
25-
<ch.qos.logback.version>1.4.14</ch.qos.logback.version>
26-
<org.eclipse.rdf4j.version>4.3.10</org.eclipse.rdf4j.version>
24+
<org.junit.jupiter.version>5.11.0</org.junit.jupiter.version>
25+
<org.mockito.version>5.12.0</org.mockito.version>
26+
<ch.qos.logback.version>1.5.7</ch.qos.logback.version>
27+
<org.eclipse.rdf4j.version>5.0.2</org.eclipse.rdf4j.version>
2728
</properties>
2829

2930
<dependencies>
@@ -79,13 +80,13 @@
7980
<dependency>
8081
<groupId>com.apicatalog</groupId>
8182
<artifactId>titanium-json-ld</artifactId>
82-
<version>1.4.0</version>
83+
<version>1.4.1</version>
8384
<scope>test</scope>
8485
</dependency>
8586
<dependency>
8687
<groupId>org.eclipse.parsson</groupId>
8788
<artifactId>jakarta.json</artifactId>
88-
<version>1.1.5</version>
89+
<version>1.1.7</version>
8990
<scope>test</scope>
9091
</dependency>
9192
<dependency>
@@ -152,18 +153,14 @@
152153
</executions>
153154
</plugin>
154155
<plugin>
155-
<!-- explicitly define maven-deploy-plugin after other to force exec order -->
156-
<artifactId>maven-deploy-plugin</artifactId>
157-
<version>3.0.0</version>
158-
<executions>
159-
<execution>
160-
<id>deploy</id>
161-
<phase>deploy</phase>
162-
<goals>
163-
<goal>deploy</goal>
164-
</goals>
165-
</execution>
166-
</executions>
156+
<groupId>org.sonatype.central</groupId>
157+
<artifactId>central-publishing-maven-plugin</artifactId>
158+
<version>0.5.0</version>
159+
<extensions>true</extensions>
160+
<configuration>
161+
<publishingServerId>central</publishingServerId>
162+
<autoPublish>true</autoPublish>
163+
</configuration>
167164
</plugin>
168165
</plugins>
169166
</build>

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public enum ConfigParam {
3939
/**
4040
* Whether to require an identifier when serializing an object.
4141
* <p>
42-
* If set to {@code true} and no identifier is found (either there is no identifier field or its value is {@code
43-
* null}), an exception will be thrown. If configured to {@code false}, a blank node identifier is generated if no
44-
* id is present.
42+
* If set to {@code true} and no identifier is found (either there is no identifier field or its value is
43+
* {@code null}), an exception will be thrown. If configured to {@code false}, a blank node identifier is generated
44+
* if no id is present.
4545
*/
4646
REQUIRE_ID("requireId"),
4747

@@ -59,8 +59,8 @@ public enum ConfigParam {
5959
* Enables optimistic target type resolution.
6060
* <p>
6161
* This means that when a an ambiguous target type is encountered during deserialization of an object (i.e.,
62-
* multiple concrete classes match the data type), instead of throwing an {@link
63-
* cz.cvut.kbss.jsonld.exception.AmbiguousTargetTypeException}, one of the classes will be selected for
62+
* multiple concrete classes match the data type), instead of throwing an
63+
* {@link cz.cvut.kbss.jsonld.exception.AmbiguousTargetTypeException}, one of the classes will be selected for
6464
* instantiation.
6565
* <p>
6666
* Note that enabling this behavior should probably be done together with setting {@link #IGNORE_UNKNOWN_PROPERTIES}
@@ -98,19 +98,34 @@ public enum ConfigParam {
9898
/**
9999
* Format string used to serialize and deserialize datetime values.
100100
* <p>
101-
* Note that if {@link #SERIALIZE_DATETIME_AS_MILLIS} is enabled, this parameter has no effect on serialization of datetime.
101+
* Note that if {@link #SERIALIZE_DATETIME_AS_MILLIS} is enabled, this parameter has no effect on serialization of
102+
* datetime.
102103
* <p>
103-
* Also note that this format applies only to full datetime values. Date or time values have to be formatted per-attribute.
104+
* Also note that this format applies only to full datetime values. Date or time values have to be formatted
105+
* per-attribute.
104106
*/
105107
DATE_TIME_FORMAT("datetimeFormat"),
106108

107109
/**
108110
* Whether to serialize individuals using expanded term definition in context.
109-
*
111+
* <p>
110112
* This basically means that the individual's identifier is provided directly as a string and an expanded term
111-
* definition (consisting of a {@literal @id} and {@literal @type}) is added into the context, specifying that the string is an identifier.
113+
* definition (consisting of a {@literal @id} and {@literal @type}) is added into the context, specifying that the
114+
* string is an identifier.
115+
*/
116+
SERIALIZE_INDIVIDUALS_USING_EXPANDED_DEFINITION("serializeIndividualsUsingExpandedDefinition"),
117+
118+
/**
119+
* Whether to disable the type map cache.
120+
* <p>
121+
* Type map is used to map JSON-LD types to Java classes for deserialization. Since this map is built by scanning
122+
* the classpath (see the {@link #SCAN_PACKAGE} parameter), it is cached by default to avoid repeated scanning of
123+
* the classpath.
124+
* <p>
125+
* If every deserializer instance should get a fresh type map based on the current configuration, disable this
126+
* cache.
112127
*/
113-
SERIALIZE_INDIVIDUALS_USING_EXPANDED_DEFINITION("serializeIndividualsUsingExpandedDefinition");
128+
DISABLE_TYPE_MAP_CACHE("disableTypeMapCache");
114129

115130
private final String name;
116131

src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,31 @@
1717
*/
1818
package cz.cvut.kbss.jsonld.common;
1919

20+
import cz.cvut.kbss.jopa.model.annotations.Id;
21+
import cz.cvut.kbss.jopa.model.annotations.Namespace;
22+
import cz.cvut.kbss.jopa.model.annotations.Namespaces;
23+
import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty;
24+
import cz.cvut.kbss.jopa.model.annotations.OWLClass;
25+
import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty;
26+
import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty;
2027
import cz.cvut.kbss.jopa.model.annotations.Properties;
21-
import cz.cvut.kbss.jopa.model.annotations.*;
28+
import cz.cvut.kbss.jopa.model.annotations.Types;
2229
import cz.cvut.kbss.jsonld.JsonLd;
2330
import cz.cvut.kbss.jsonld.annotation.JsonLdAttributeOrder;
2431
import cz.cvut.kbss.jsonld.exception.JsonLdSerializationException;
2532

2633
import java.lang.reflect.AnnotatedElement;
2734
import java.lang.reflect.Field;
2835
import java.lang.reflect.Modifier;
29-
import java.util.*;
36+
import java.util.ArrayList;
37+
import java.util.Arrays;
38+
import java.util.Comparator;
39+
import java.util.HashSet;
40+
import java.util.List;
41+
import java.util.Map;
42+
import java.util.Objects;
43+
import java.util.Optional;
44+
import java.util.Set;
3045
import java.util.function.BiPredicate;
3146
import java.util.function.Function;
3247
import java.util.stream.Collectors;
@@ -126,7 +141,7 @@ private static Optional<String> expandIri(String iri, Class<?> declaringClass) {
126141
}
127142
}
128143
return declaringClass.getSuperclass() != null ? expandIri(iri, declaringClass.getSuperclass()) :
129-
Optional.empty();
144+
Optional.empty();
130145
}
131146

132147
private static Optional<String> resolveNamespace(AnnotatedElement annotated, String prefix) {
@@ -418,6 +433,14 @@ public static String getAttributeIdentifier(Field field) {
418433
throw new JsonLdSerializationException("Field " + field + " is not JSON-LD serializable.");
419434
}
420435

436+
/**
437+
* Gets the identifier field of the specified class.
438+
* <p>
439+
* That is, gets the field annotated with {@link Id}, even inherited.
440+
*
441+
* @param cls Class whose identifier field to retrieve
442+
* @return Matching field, optionally empty
443+
*/
421444
public static Optional<Field> getIdentifierField(Class<?> cls) {
422445
return getMarshallableFields(cls, (f, c) -> f.isAnnotationPresent(Id.class)).stream().findFirst();
423446
}

src/main/java/cz/cvut/kbss/jsonld/deserialization/InstanceBuilder.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import cz.cvut.kbss.jsonld.exception.JsonLdDeserializationException;
2424

2525
import java.util.List;
26-
import java.util.Optional;
2726

2827
/**
2928
* Builds instances from parsed JSON-LD.
@@ -58,8 +57,8 @@ public interface InstanceBuilder {
5857
* <p>
5958
* The new instance also becomes the currently open object.
6059
* <p>
61-
* This method is intended for creating top level objects or adding objects to collections. Use {@link
62-
* #openObject(String, String, List)} for opening objects as values of attributes.
60+
* This method is intended for creating top level objects or adding objects to collections. Use
61+
* {@link #openObject(String, String, List)} for opening objects as values of attributes.
6362
*
6463
* @param <T> The type of the object to open
6564
* @param id Identifier of the object being open
@@ -82,8 +81,8 @@ public interface InstanceBuilder {
8281
* <p>
8382
* The new collection also becomes the currently open object.
8483
* <p>
85-
* This method should also verify cardinality, i.e. a collection cannot be set as value of a field mapped by {@code
86-
* property}, if the field is singular.
84+
* This method should also verify cardinality, i.e. a collection cannot be set as value of a field mapped by
85+
* {@code property}, if the field is singular.
8786
* <p>
8887
* This method assumes that the property is mapped, i.e. that {@link #isPropertyMapped(String)} returned true.
8988
*
@@ -99,8 +98,8 @@ public interface InstanceBuilder {
9998
* <p>
10099
* The new collection also becomes the currently open object.
101100
* <p>
102-
* This method is intended for creating top level collections or nesting collections. Use {@link
103-
* #openCollection(String)} for opening collections as values of attributes.
101+
* This method is intended for creating top level collections or nesting collections. Use
102+
* {@link #openCollection(String)} for opening collections as values of attributes.
104103
*
105104
* @param collectionType Type of the JSON collection to instantiate in Java
106105
* @see #openCollection(String)
@@ -188,8 +187,8 @@ public interface InstanceBuilder {
188187
* Checks whether the current collection context represents a {@link cz.cvut.kbss.jopa.model.annotations.Properties}
189188
* attribute.
190189
*
191-
* @return {@code true} if the current context is a collection representing a {@link
192-
* cz.cvut.kbss.jopa.model.annotations.Properties} field
190+
* @return {@code true} if the current context is a collection representing a
191+
* {@link cz.cvut.kbss.jopa.model.annotations.Properties} field
193192
*/
194193
boolean isCurrentCollectionProperties();
195194

@@ -239,11 +238,12 @@ public interface InstanceBuilder {
239238

240239
/**
241240
* Gets the target type of the specified property.
242-
*
243-
* That is, it retrieves the type of the field to which the specified property is mapped. If the target field is a collection,
244-
* the element type is returned (equivalent to {@link #getCurrentCollectionElementType()}).
245-
*
241+
* <p>
242+
* That is, it retrieves the type of the field to which the specified property is mapped. If the target field is a
243+
* collection, the element type is returned (equivalent to {@link #getCurrentCollectionElementType()}).
244+
* <p>
246245
* This method assumes that the property is mapped, i.e. that {@link #isPropertyMapped(String)} returned true.
246+
*
247247
* @param property Property whose target type to resolve
248248
* @return Target type wrapped in an optional
249249
*/

src/main/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializer.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
*/
3737
public abstract class JsonLdDeserializer implements Configured {
3838

39+
private static final TypeMap TYPE_MAP = new TypeMap();
40+
3941
private final Configuration configuration;
4042

4143
protected final TargetClassResolver classResolver;
@@ -53,19 +55,33 @@ protected JsonLdDeserializer(Configuration configuration) {
5355
}
5456

5557
private TargetClassResolver initializeTargetClassResolver() {
56-
final TypeMap typeMap = new TypeMap();
5758
final String scanPath = configuration.get(ConfigParam.SCAN_PACKAGE, "");
59+
final TypeMap typeMap = discoverAvailableTypes(scanPath, configuration.is(ConfigParam.DISABLE_TYPE_MAP_CACHE));
60+
return new TargetClassResolver(typeMap,
61+
new TargetClassResolverConfig(
62+
configuration.is(ConfigParam.ASSUME_TARGET_TYPE),
63+
configuration().is(ConfigParam.ENABLE_OPTIMISTIC_TARGET_TYPE_RESOLUTION),
64+
configuration().is(ConfigParam.PREFER_SUPERCLASS)));
65+
}
66+
67+
/**
68+
* Finds potential deserialization target types on the classpath.
69+
*
70+
* @param scanPath Path to scan on classpath
71+
* @return Map of types to Java classes
72+
*/
73+
private static TypeMap discoverAvailableTypes(String scanPath, boolean disableCache) {
74+
final TypeMap map = disableCache ? new TypeMap() : TYPE_MAP;
75+
if (!map.isEmpty()) {
76+
return map;
77+
}
5878
new ClasspathScanner(c -> {
5979
final OWLClass ann = c.getDeclaredAnnotation(OWLClass.class);
6080
if (ann != null) {
61-
typeMap.register(BeanAnnotationProcessor.expandIriIfNecessary(ann.iri(), c), c);
81+
map.register(BeanAnnotationProcessor.expandIriIfNecessary(ann.iri(), c), c);
6282
}
6383
}).processClasses(scanPath);
64-
return new TargetClassResolver(typeMap,
65-
new TargetClassResolverConfig(
66-
configuration.is(ConfigParam.ASSUME_TARGET_TYPE),
67-
configuration().is(ConfigParam.ENABLE_OPTIMISTIC_TARGET_TYPE_RESOLUTION),
68-
configuration().is(ConfigParam.PREFER_SUPERCLASS)));
84+
return map;
6985
}
7086

7187
@Override
@@ -78,9 +94,9 @@ public Configuration configuration() {
7894
* <p>
7995
* If a deserializer already existed for the type, it is replaced by the new one.
8096
*
81-
* @param type Target type to register the deserializer for
97+
* @param type Target type to register the deserializer for
8298
* @param deserializer Deserializer to register
83-
* @param <T> Target type
99+
* @param <T> Target type
84100
*/
85101
public <T> void registerDeserializer(Class<T> type, ValueDeserializer<T> deserializer) {
86102
Objects.requireNonNull(type);

src/main/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
*/
1818
package cz.cvut.kbss.jsonld.deserialization.expanded;
1919

20+
import cz.cvut.kbss.jsonld.ConfigParam;
2021
import cz.cvut.kbss.jsonld.Configuration;
2122
import cz.cvut.kbss.jsonld.deserialization.DefaultInstanceBuilder;
2223
import cz.cvut.kbss.jsonld.deserialization.DeserializationContext;
2324
import cz.cvut.kbss.jsonld.deserialization.InstanceBuilder;
2425
import cz.cvut.kbss.jsonld.deserialization.JsonLdDeserializer;
26+
import cz.cvut.kbss.jsonld.deserialization.reference.AssumedTypeReferenceReplacer;
2527
import cz.cvut.kbss.jsonld.deserialization.reference.PendingReferenceRegistry;
2628
import cz.cvut.kbss.jsonld.exception.JsonLdDeserializationException;
2729
import jakarta.json.JsonArray;
@@ -59,6 +61,9 @@ public <T> T deserialize(JsonValue jsonLd, Class<T> resultClass) {
5961
final InstanceBuilder instanceBuilder = new DefaultInstanceBuilder(classResolver, referenceRegistry);
6062
new ObjectDeserializer(instanceBuilder, new DeserializerConfig(configuration(), classResolver, deserializers), resultClass)
6163
.processValue(root);
64+
if (configuration().is(ConfigParam.ASSUME_TARGET_TYPE)) {
65+
new AssumedTypeReferenceReplacer().replacePendingReferencesWithAssumedTypedObjects(referenceRegistry);
66+
}
6267
referenceRegistry.verifyNoUnresolvedReferencesExist();
6368
assert resultClass.isAssignableFrom(instanceBuilder.getCurrentRoot().getClass());
6469
return resultClass.cast(instanceBuilder.getCurrentRoot());

0 commit comments

Comments
 (0)