Skip to content
This repository was archived by the owner on Feb 15, 2024. It is now read-only.

Commit 7d81968

Browse files
author
Matthias Böckmann
committed
Initial commit of RDF Serializer
Not yet functional, as serialization has no IDs
1 parent 25936a3 commit 7d81968

85 files changed

Lines changed: 3687 additions & 8 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dataformat-json/src/main/java/io/adminshell/aas/v3/dataformat/json/ReflectionAnnotationIntrospector.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,13 @@
3434
* This class helps to dynamically decide how to de-/serialize classes and
3535
* properties defined in the AAS model library.
3636
*
37-
* This is equivialent to adding the following annotations
37+
* This is equivalent to adding the following annotations
3838
* <ul>
3939
* <li> to all interfaces defined in the AAS model:
4040
* <ul>
4141
* <li> @JsonTypeName([interface name])
4242
* <li> @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "modelType")
43-
* <li> @JsonSubTypes({
44-
*
45-
* @Type(value = [sub-interface].class, name = "[sub-interface name]"), ...})
43+
* <li> @JsonSubTypes({@Type(value = [sub-interface].class, name = "[sub-interface name]"), ...})
4644
* for each sub-interface
4745
* </ul>
4846
* <li> to all getter methods returning any type of Collection<?> defined in the
@@ -71,7 +69,7 @@ private Class<?> getMostSpecificTypeWithModelType(Class<?> clazz) {
7169
.filter(x -> clazz.isInterface() ? x.equals(clazz) : x.isAssignableFrom(clazz))
7270
.sorted((Class<?> o1, Class<?> o2) -> {
7371
// -1: o1 more special than o2
74-
// 0: o1 equals o2 or on same samelevel
72+
// 0: o1 equals o2 or on same same level
7573
// 1: o2 more special than o1
7674
if (o1.isAssignableFrom(o2)) {
7775
if (o2.isAssignableFrom(o1)) {

dataformat-jsonld/pom.xml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>dataformat-parent</artifactId>
7+
<groupId>io.admin-shell.aas</groupId>
8+
<version>${revision}</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>dataformat-jsonld</artifactId>
13+
14+
<properties>
15+
<maven.compiler.source>12</maven.compiler.source>
16+
<maven.compiler.target>12</maven.compiler.target>
17+
</properties>
18+
19+
<dependencies>
20+
<dependency>
21+
<groupId>io.admin-shell.aas</groupId>
22+
<artifactId>dataformat-core</artifactId>
23+
<version>${revision}</version>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.apache.jena</groupId>
27+
<artifactId>jena-arq</artifactId>
28+
<version>${jena.version}</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>junit</groupId>
32+
<artifactId>junit</artifactId>
33+
<version>${junit.version}</version>
34+
<scope>test</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.slf4j</groupId>
38+
<artifactId>slf4j-api</artifactId>
39+
<version>${slf4j.version}</version>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.slf4j</groupId>
43+
<artifactId>slf4j-log4j12</artifactId>
44+
<version>1.7.31</version>
45+
<scope>test</scope>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.apache.logging.log4j</groupId>
49+
<artifactId>log4j-core</artifactId>
50+
<version>2.14.1</version>
51+
<scope>test</scope>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.apache.logging.log4j</groupId>
55+
<artifactId>log4j-api</artifactId>
56+
<version>2.14.1</version>
57+
<scope>test</scope>
58+
</dependency>
59+
60+
</dependencies>
61+
62+
63+
</project>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.adminshell.aas.v3.dataformat.jsonld;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.databind.SerializerProvider;
5+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
6+
7+
import java.io.IOException;
8+
import java.util.Map;
9+
10+
public class FallbackSerializer extends StdSerializer<Map<String, Object>> {
11+
12+
13+
public FallbackSerializer() {
14+
this(null);
15+
}
16+
17+
public FallbackSerializer(Class clazz) {
18+
super(clazz);
19+
}
20+
21+
22+
@Override
23+
public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
24+
gen.writeString(value.toString());
25+
}
26+
27+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.adminshell.aas.v3.dataformat.jsonld;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreType;
4+
5+
@JsonIgnoreType
6+
public class IgnoreTypeMixIn {
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.adminshell.aas.v3.dataformat.jsonld;
2+
3+
import com.fasterxml.jackson.databind.module.SimpleModule;
4+
5+
import io.adminshell.aas.v3.dataformat.jsonld.custom.BigDecimalSerializer;
6+
import io.adminshell.aas.v3.dataformat.jsonld.custom.XMLGregorianCalendarDeserializer;
7+
import io.adminshell.aas.v3.dataformat.jsonld.custom.XMLGregorianCalendarSerializer;
8+
9+
import java.math.BigDecimal;
10+
import java.net.URI;
11+
12+
13+
import javax.xml.datatype.XMLGregorianCalendar;
14+
15+
16+
/**
17+
* Jackson module which provides support for JSON-LD serialization
18+
*/
19+
public class JsonLDModule extends SimpleModule {
20+
21+
public JsonLDModule() {
22+
super();
23+
24+
25+
setSerializerModifier(new JsonLDSerializerModifier());
26+
27+
addSerializer(XMLGregorianCalendar.class, new XMLGregorianCalendarSerializer());
28+
addDeserializer(XMLGregorianCalendar.class, new XMLGregorianCalendarDeserializer());
29+
addSerializer(BigDecimal.class, new BigDecimalSerializer());
30+
31+
addSerializer(URI.class, new UriSerializer());
32+
}
33+
34+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package io.adminshell.aas.v3.dataformat.jsonld;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import com.fasterxml.jackson.core.JsonGenerator;
6+
import com.fasterxml.jackson.core.JsonToken;
7+
import com.fasterxml.jackson.core.type.WritableTypeId;
8+
import com.fasterxml.jackson.databind.SerializerProvider;
9+
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
10+
import com.fasterxml.jackson.databind.ser.BeanSerializer;
11+
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
12+
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import java.io.IOException;
17+
import java.lang.reflect.Field;
18+
import java.math.BigInteger;
19+
import java.util.*;
20+
import java.util.stream.Stream;
21+
22+
23+
public class JsonLDSerializer extends BeanSerializer {
24+
25+
private final Logger logger = LoggerFactory.getLogger(JsonLDSerializer.class);
26+
27+
private static int currentRecursionDepth = 0;
28+
29+
static final Map<String, String> contextItems;
30+
31+
static {
32+
contextItems = new HashMap<>();
33+
contextItems.put("ids", "https://w3id.org/idsa/core/");
34+
contextItems.put("idsc", "https://w3id.org/idsa/code/");
35+
contextItems.put("xsd", "http://www.w3.org/2001/XMLSchema#");
36+
contextItems.put("owl", "http://www.w3.org/2002/07/owl#");
37+
contextItems.put("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
38+
contextItems.put("aas", "https://admin-shell.io/aas/3/0/RC01/");
39+
contextItems.put("iec61360", "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0/RC01/");
40+
contextItems.put("phys_unit", "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationPhysicalUnit/3/0/RC01/");
41+
}
42+
43+
44+
JsonLDSerializer(BeanSerializerBase src) {
45+
super(src);
46+
}
47+
48+
49+
50+
@Override
51+
public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
52+
gen.setCurrentValue(bean);
53+
54+
currentRecursionDepth++;
55+
gen.writeStartObject();
56+
57+
if (currentRecursionDepth == 1) {
58+
Map<String, String> filteredContext = new HashMap<>();
59+
filterContextWrtBean(bean, filteredContext);
60+
gen.writeObjectField("@context", filteredContext);
61+
//gen.writeStringField("@context", "https://jira.iais.fraunhofer.de/stash/projects/ICTSL/repos/ids-infomodel-commons/raw/jsonld-context/3.0.0/context.jsonld"); // only add @context on top level
62+
63+
}
64+
WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT);
65+
String resolvedTypeId = typeIdDef.id != null ? typeIdDef.id.toString() : typeSer.getTypeIdResolver().idFromValue(bean);
66+
if (resolvedTypeId != null) {
67+
gen.writeStringField(typeIdDef.asProperty, resolvedTypeId);
68+
}
69+
if (_propertyFilterId != null) {
70+
serializeFieldsFiltered(bean, gen, provider);
71+
} else {
72+
serializeFields(bean, gen, provider);
73+
}
74+
gen.writeEndObject();
75+
currentRecursionDepth--;
76+
}
77+
78+
79+
private void filterContextWrtBean(Object bean, Map<String, String> filteredContext) {
80+
//Some default entries for AAS
81+
filteredContext.put("aas", "https://admin-shell.io/aas/3/0/RC01/");
82+
filteredContext.put("iec61360", "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0/RC01/");
83+
filteredContext.put("phys_unit", "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationPhysicalUnit/3/0/RC01/");
84+
85+
if(bean == null || bean.getClass().getName().equals("com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl") || bean.getClass().getName().equals("org.apache.jena.ext.xerces.jaxp.datatype.XMLGregorianCalendarImpl") || bean.getClass() == BigInteger.class) return; // XMLGregorianCalendarImpl causes infinite recursion
86+
87+
//Check if RdfResource or TypedLiteral is used. They contain a field called "type" which can reference to any namespace
88+
//Therefore it is vital to also check the value of the type field for prefixes that need to be included in the context
89+
if(bean.getClass().getSimpleName().equals("LangString"))
90+
{
91+
//LangString is of type rdf:langString, so this must be present
92+
filteredContext.put("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
93+
}
94+
contextItems.forEach((p, u) -> {
95+
JsonTypeName typeNameAnnotation = bean.getClass().getAnnotation(JsonTypeName.class);
96+
if(typeNameAnnotation != null && typeNameAnnotation.value().contains(p)) {
97+
filteredContext.put(p, u);
98+
}
99+
Stream.of(bean.getClass().getMethods()).forEach(m -> {
100+
JsonProperty propertyAnnotation = m.getAnnotation(JsonProperty.class);
101+
if(propertyAnnotation != null && propertyAnnotation.value().contains(p)) {
102+
filteredContext.put(p, u);
103+
}
104+
});
105+
});
106+
Stream.of(bean.getClass().getMethods()).forEach(m -> {
107+
// run though all properties and check annotations. These annotations should contain the prefixes
108+
JsonProperty prop = m.getAnnotation(JsonProperty.class);
109+
if(prop != null)
110+
{
111+
for(Map.Entry<String, String> entry : contextItems.entrySet())
112+
{
113+
if(prop.value().startsWith(entry.getKey()))
114+
{
115+
filteredContext.put(entry.getKey(), entry.getValue());
116+
break;
117+
}
118+
}
119+
}
120+
});
121+
// run through fields recursively
122+
for(Field f : getAllFields(new HashSet<>(), bean.getClass())) {
123+
124+
if(Collection.class.isAssignableFrom(f.getType()))
125+
{
126+
try {
127+
if(f.getType().getName().startsWith("java.") && !f.getType().getName().startsWith("java.util")) continue;
128+
boolean accessible = f.isAccessible();
129+
f.setAccessible(true);
130+
Collection<?> c = (Collection<?>) f.get(bean);
131+
if(c == null) {
132+
continue;
133+
}
134+
for(Object o : c)
135+
{
136+
filterContextWrtBean(o, filteredContext);
137+
}
138+
f.setAccessible(accessible);
139+
}
140+
catch (IllegalAccessException e) {
141+
e.printStackTrace();
142+
}
143+
}
144+
145+
if (f.getType().isPrimitive() || f.getType().isEnum() || f.getType().isArray()
146+
|| f.getType().getName().contains("java.")
147+
|| f.getType().getName().contains("javax.")) continue;
148+
149+
try {
150+
boolean wasAccessible = f.isAccessible();
151+
f.setAccessible(true);
152+
filterContextWrtBean(f.get(bean), filteredContext);
153+
f.setAccessible(wasAccessible);
154+
} catch (IllegalAccessException ignored) {
155+
//logger.error("setting accessible failed"); //We can catch that here, as IllegalReflectiveAccess cannot occur on our own packages
156+
}
157+
158+
//f.trySetAccessible(wasAccessible);
159+
160+
}
161+
162+
}
163+
164+
/**
165+
* This function retrieves a set of all available fields of a class, including inherited fields
166+
* @param fields Set to which discovered fields will be added. An empty HashSet should do the trick
167+
* @param type The class for which fields should be discovered
168+
* @return set of all available fields
169+
*/
170+
private static Set<Field> getAllFields(Set<Field> fields, Class<?> type) {
171+
fields.addAll(Arrays.asList(type.getDeclaredFields()));
172+
173+
if (type.getSuperclass() != null) {
174+
getAllFields(fields, type.getSuperclass());
175+
}
176+
177+
return fields;
178+
}
179+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.adminshell.aas.v3.dataformat.jsonld;
2+
3+
4+
import com.fasterxml.jackson.databind.BeanDescription;
5+
import com.fasterxml.jackson.databind.JsonSerializer;
6+
import com.fasterxml.jackson.databind.SerializationConfig;
7+
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
8+
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
9+
import io.adminshell.aas.v3.dataformat.jsonld.JsonLDSerializer;
10+
11+
12+
public class JsonLDSerializerModifier extends BeanSerializerModifier {
13+
14+
15+
public JsonLDSerializerModifier() {
16+
}
17+
18+
@Override
19+
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
20+
if (serializer instanceof BeanSerializerBase) {
21+
return new JsonLDSerializer((BeanSerializerBase) serializer);
22+
} else {
23+
return serializer;
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)