diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractCollectionMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractCollectionMapper.java
new file mode 100644
index 000000000..ecbd2555b
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractCollectionMapper.java
@@ -0,0 +1,79 @@
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Referable;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Base class for mapping collections of submodel elements as for
+ *
+ * - {@link SubmodelMapper}
+ * - {@link ElementsCollectionMapper}
+ *
+ * @param
+ */
+public abstract class AbstractCollectionMapper extends AbstractListMapper {
+ /**
+ * @param element the submodel element that has to be mapped.
+ * @param values
+ * @param idShortPath the idShort path is a dot separated chain of idShorts, that can be used in case of
+ * troubleshooting.
+ */
+ protected AbstractCollectionMapper(T element, List values, String idShortPath) {
+ super(element, values, idShortPath);
+ }
+
+ ObjectNode valuesToJson() throws ValueOnlySerializationException {
+ ObjectNode elementsNode = JsonNodeFactory.instance.objectNode();
+ for (SubmodelElement submodelElement : values) {
+ String idShort = submodelElement.getIdShort();
+ if (elementsNode.has(idShort)) {
+ throw new ValueOnlySerializationException("Duplicated idShort name '" + idShort +
+ "' under idShort path '" + idShortPath + "'.", idShortPath);
+ }
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(submodelElement, idShortPath + "." + idShort);
+ if (mapper == null) {
+ // This type of submodel elements are not serialized in value-only format.
+ continue;
+ }
+ JsonNode mapperNode = mapper.toJson();
+ if (elementsNode.isObject()) {
+ elementsNode.setAll((ObjectNode) mapperNode);
+ } else {
+ elementsNode.set(idShort, mapperNode);
+ }
+ }
+ return elementsNode;
+ }
+
+ public void updateFromJson(JsonNode value) throws ValueOnlySerializationException {
+ if (!value.isObject()) {
+ throw new ValueOnlySerializationException(
+ "Cannot update the submodel elements collection at idShort path '" + idShortPath +
+ "', as the corresponding value-only is not a JSON object.", idShortPath);
+ }
+ ObjectNode objNode = (ObjectNode) value;
+ for (Iterator it = objNode.fieldNames(); it.hasNext(); ) {
+ String idShort = it.next();
+ SubmodelElement submodelElement = findElementByIdShort(idShort);
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(submodelElement, idShortPath + "." + idShort);
+ //mapper.update(objNode.get(idShort));
+ mapper.update(JsonNodeFactory.instance.objectNode().set(idShort, objNode.get(idShort)));
+ }
+ }
+
+ private SubmodelElement findElementByIdShort(String idShort) throws ValueOnlySerializationException {
+ for (SubmodelElement submodelElement : values) {
+ if (idShort.equals(submodelElement.getIdShort())) {
+ return submodelElement;
+ }
+ }
+ throw new ValueOnlySerializationException("Cannot find submodel element with idShort '" + idShort +
+ "' at idShort path '" + idShortPath + "'.", idShortPath);
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractListMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractListMapper.java
new file mode 100644
index 000000000..aa28217e4
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractListMapper.java
@@ -0,0 +1,28 @@
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Referable;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+
+/**
+ * Base class for mapping elements providing list of submodel elements as for
+ *
+ * - {@link ElementsListMapper}
+ * - {@link ElementsCollectionMapper}
+ *
+ * @param
+ */
+public abstract class AbstractListMapper extends AbstractMapper {
+ protected final List values;
+
+ /**
+ * @param element the submodel element that has to be mapped.
+ * @param idShortPath the idShort path is a dot separated chain of idShorts, that can be used in case of
+ * troubleshooting.
+ */
+ protected AbstractListMapper(T element, List values, String idShortPath) {
+ super(element, idShortPath);
+ this.values = values;
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractMapper.java
new file mode 100644
index 000000000..7c6abffcc
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AbstractMapper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.Iterator;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Referable;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+
+/**
+ * The abstract base class for all value-only mappers.
+ * @param The type of the mapped elements.
+ */
+abstract class AbstractMapper implements ValueOnlyMapper {
+ protected final T element;
+ protected final String idShortPath;
+
+ /**
+ *
+ * @param element the submodel element that has to be mapped.
+ * @param idShortPath the idShort path is a dot separated chain of idShorts, that can be used in case of
+ * troubleshooting.
+ */
+ protected AbstractMapper(T element, String idShortPath) {
+ this.element = element;
+ this.idShortPath = idShortPath;
+ }
+
+ JsonNode asValueNode(JsonNode value) {
+ return JsonNodeFactory.instance.objectNode().set(element.getIdShort(), value);
+ }
+
+ static JsonNode valueFromNode(String msg, String idShortPath, JsonNode valueOnly) {
+ Iterator fieldNames = valueOnly.fieldNames();
+ if (!fieldNames.hasNext()) {
+ // throw exception as value-only nodes must have exactly one field!
+ throw new ValueOnlySerializationException(
+ msg + " at idShort path '" + idShortPath +
+ "', as the passed value does have no fields!", idShortPath);
+ }
+ String fieldName = fieldNames.next();
+ if (fieldNames.hasNext()) {
+ // throw exception as value-only nodes must have exactly one field!
+ throw new ValueOnlySerializationException(
+ msg + " at idShort path '" + idShortPath +
+ "', as the passed value has more than one field!", idShortPath);
+ }
+ return valueOnly.get(fieldName);
+ }
+
+ /**
+ * Verifies the given object is neither an object nor an array
+ * @param msg Prefix for exception messages.
+ * @param idShortPath short path of ID being included to exception messages.
+ * @param value the node to return as text
+ * @return given {@code value} as text if it's neither an object nor an array, otherwise throws exception.
+ * @throws ValueOnlySerializationException throw if node is object or array.
+ */
+ String readValueAsString(String msg, String idShortPath, JsonNode value)
+ throws ValueOnlySerializationException {
+ if (value == null || value.isNull()) {
+ return null;
+ }
+ if (value.isObject()) {
+ throw new ValueOnlySerializationException(
+ msg + " at idShort path '" + idShortPath +
+ "', as the passed value is a JSON object.", idShortPath);
+ }
+ if (value.isArray()) {
+ throw new ValueOnlySerializationException(
+ msg + " at idShort path '" + idShortPath +
+ "', as the passed value is a JSON array.", idShortPath);
+ }
+ return value.asText();
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AnnotatedRelationshipMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AnnotatedRelationshipMapper.java
new file mode 100644
index 000000000..b38d2040e
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/AnnotatedRelationshipMapper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AnnotatedRelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.DataElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * AnnotatedRelationshipElement is serialized according to the serialization of a ReleationshipElement. Additionally, a
+ * third named JSON object is introduced with "annotations" as the name of the containing JSON property. The value is
+ * ${AnnotatedRelationshipElement/annotations}. The values of the array items are serialized depending on the type of
+ * the annotation data element.
+ */
+class AnnotatedRelationshipMapper extends RelationshipElementMapper {
+ private static final String ANNOTATIONS = "annotations";
+ AnnotatedRelationshipMapper(AnnotatedRelationshipElement relationship, String idShortPath) {
+ super(relationship, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode valueNode = (ObjectNode) super.toJson();
+ AnnotatedRelationshipElement annotatedRelationshipElement = (AnnotatedRelationshipElement) element;
+ List annotations = new ArrayList<>(annotatedRelationshipElement.getAnnotations());
+ if(!annotations.isEmpty()) {
+ ElementsListMapper listMapper = new ElementsListMapper<>(
+ annotatedRelationshipElement, annotations, idShortPath + "." + ANNOTATIONS);
+ ObjectNode dataNode = (ObjectNode) valueNode.get(element.getIdShort());
+ dataNode.set(ANNOTATIONS, listMapper.toJson());
+ }
+ return valueNode;
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ super.update(valueOnly);
+ AnnotatedRelationshipElement annotatedRelationshipElement = (AnnotatedRelationshipElement) element;
+ List annotations = annotatedRelationshipElement.getAnnotations();
+ JsonNode value = valueFromNode("Cannot update the annotated relationship", idShortPath, valueOnly);
+ JsonNode annotationsNode = value.get(ANNOTATIONS);
+ if(annotationsNode == null || annotationsNode.isNull()) {
+ annotations.clear();
+ } else {
+ List elements = new ArrayList<>(annotations);
+ ElementsListMapper listMapper = new ElementsListMapper<>(annotatedRelationshipElement, elements, idShortPath + "." + ANNOTATIONS);
+ listMapper.update(annotationsNode);
+ }
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/BasicEventElementMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/BasicEventElementMapper.java
new file mode 100644
index 000000000..d5a12b99b
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/BasicEventElementMapper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.BasicEventElement;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * BasicEventElement is serialized as named JSON object with ${BasicEventElement/idShort} as the name of the containing
+ * JSON property. The JSON object contains one JSON property named “observed” with the corresponding value of
+ * ${BasicEventElement/observed} as the standard serialization of the Reference class.
+ */
+class BasicEventElementMapper extends AbstractMapper {
+ private static final String OBSERVED = "observed";
+
+ BasicEventElementMapper(BasicEventElement event, String idShortPath) {
+ super(event, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ node.set(OBSERVED, new JsonValueOnlyDeserialiser().toJson(element.getObserved()));
+ return node;
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ element.setObserved(new JsonValueOnlyDeserialiser().deserialiseReference(valueOnly.get(OBSERVED), idShortPath));
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/BlobMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/BlobMapper.java
new file mode 100644
index 000000000..6dae8a659
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/BlobMapper.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.Base64;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Blob;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+/**
+ * Blob is serialized as named JSON object with ${Blob/idShort} as the name of the containing JSON property. The JSON
+ * object contains two JSON properties. The first refers to the content type named "contentType" and value
+ * ${Blob/contentType}. The latter refers to the value named “value” and value ${Blob/value}. The resulting value-only
+ * object is indistinguishable whether it contains File or Blob attributes. Therefore, the receiver needs to take the
+ * type of the target resource into account. Since the receiver knows in advance if a File or a Blob SubmodelElement
+ * shall be manipulated, it can parse the transferred value-only object accordingly as a File or Blob object.
+ */
+class BlobMapper extends AbstractMapper {
+ private static final String CONTENT_TYPE = "contentType";
+ private static final String VALUE = "value";
+
+ BlobMapper(Blob blob, String idShortPath) {
+ super(blob, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ node.set(CONTENT_TYPE, new TextNode(element.getContentType()));
+ node.set(VALUE, new TextNode(Base64.getEncoder().encodeToString(element.getValue())));
+ return node;
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode contentNode = valueOnly.get(CONTENT_TYPE);
+ if(contentNode == null || contentNode.isNull()) {
+ element.setContentType(null);
+ } else if(contentNode.isTextual()) {
+ element.setContentType(contentNode.asText());
+ } else {
+ throw new ValueOnlySerializationException(
+ "Invalid Blob contentType at idShort path '" + idShortPath + "'.", idShortPath);
+ }
+
+ JsonNode valueNode = valueOnly.get(VALUE);
+ if(valueNode == null || valueNode.isNull()) {
+ element.setValue(null);
+ } else if(contentNode != null && contentNode.isTextual()) {
+ element.setValue(Base64.getDecoder().decode(valueNode.asText()));
+ } else {
+ throw new ValueOnlySerializationException(
+ "Invalid Blob value at idShort path '" + idShortPath + "'.", idShortPath);
+ }
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ElementsCollectionMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ElementsCollectionMapper.java
new file mode 100644
index 000000000..80f732f5a
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ElementsCollectionMapper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Referable;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * SubmodelElementCollection is serialized as named JSON object with ${SubmodelElementCollection/idShort} as the name of
+ * the containing JSON property. The elements contained within the struct are serialized according to their respective
+ * type with ${SubmodelElement/idShort} as the name of the containing JSON property.
+ */
+class ElementsCollectionMapper extends AbstractCollectionMapper {
+ ElementsCollectionMapper(Referable elementCollection, List values, String idShortPath) {
+ super(elementCollection, values, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode elementsNode = valuesToJson();
+ return asValueNode(elementsNode);
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode value = valueFromNode("Cannot update submodel elements collection", idShortPath, valueOnly);
+ updateFromJson(value);
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ElementsListMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ElementsListMapper.java
new file mode 100644
index 000000000..a4425273a
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ElementsListMapper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Referable;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.NullNode;
+
+/**
+ * SubmodelElementList is serialized as a JSON array with the index of the contained SubmodelElement in the list as the
+ * position in the JSON array. The elements contained within the list are serialized according to their respective type.
+ */
+class ElementsListMapper extends AbstractListMapper {
+ ElementsListMapper(T elementList, List values, String idShortPath) {
+ super(elementList, values, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ArrayNode arrayNode = JsonNodeFactory.instance.arrayNode();
+ for (int i = 0; i < values.size(); i++) {
+ SubmodelElement submodelElement = values.get(i);
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(submodelElement, idShortPath + "[" + i + "]");
+ arrayNode.add(mapper == null ? NullNode.instance : mapper.toJson());
+ }
+ return arrayNode;
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ if(!valueOnly.isArray()) {
+ throw new ValueOnlySerializationException(
+ "Cannot update the submodel elements list at idShort path '" + idShortPath +
+ "', as the corresponding value-only is not a JSON array.", idShortPath);
+ }
+ ArrayNode arrayNode = (ArrayNode) valueOnly;
+ if (arrayNode.size() != values.size()) {
+ throw new ValueOnlySerializationException(
+ "Cannot update the submodel elements list at idShort path '" + idShortPath +
+ "', as the corresponding value-only array has different size.", idShortPath);
+ }
+ for (int i = 0; i < arrayNode.size(); i++ ) {
+ SubmodelElement submodelElement = values.get(i);
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(submodelElement, idShortPath + "[" + i + "]");
+ mapper.update(arrayNode.get(i));
+ }
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/EntityMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/EntityMapper.java
new file mode 100644
index 000000000..b20d94060
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/EntityMapper.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Entity;
+import org.eclipse.digitaltwin.aas4j.v3.model.EntityType;
+import org.eclipse.digitaltwin.aas4j.v3.model.SpecificAssetId;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+import static org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.serialization.EnumSerializer.serializeEnumName;
+import static org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.deserialization.EnumDeserializer.deserializeEnumName;
+
+/**
+ * Entity is serialized as named JSON object with ${Entity/idShort} as the name of the containing JSON property. The
+ * JSON object contains three JSON properties. The first is named “statements” ${Entity/statements} and contains an
+ * array of the serialized submodel elements according to their respective serialization mentioned in this clause. The
+ * second is named either “globalAssetId” or “specificAssetId” and contains either a Reference (see above) or a
+ * SpecificAssetId. SpecificAssetId is serialized as named JSON object with the values of the properties “name” for the
+ * JSON key and “value” for the JSON value. The third property is named “entityType” and contains a string
+ * representation of ${Entity/entityType}.
+ */
+class EntityMapper extends AbstractMapper {
+ private static final String STATEMENTS = "statements";
+ private static final String GLOBAL_ASSET_ID = "globalAssetId";
+ private static final String SPECIFIC_ASSET_ID = "specificAssetId";
+ private static final String ENTITY_TYPE = "entityType";
+
+ EntityMapper(Entity entity, String idShortPath) {
+ super(entity, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ ElementsListMapper statementsMapper = new ElementsListMapper<>(
+ element, element.getStatements(), idShortPath + "." + STATEMENTS);
+ node.set(STATEMENTS, statementsMapper.toJson());
+ String globalAssetId = element.getGlobalAssetId();
+ if(globalAssetId != null) {
+ node.set(GLOBAL_ASSET_ID, new TextNode(globalAssetId));
+ }
+ List specificAssetIds = element.getSpecificAssetIds();
+ if(specificAssetIds != null && !specificAssetIds.isEmpty()) {
+ ObjectNode assetIdNode = JsonNodeFactory.instance.objectNode();
+ for (SpecificAssetId assetId : specificAssetIds) {
+ assetIdNode.set(assetId.getValue(), new TextNode(assetId.getName()));
+ }
+ node.set(SPECIFIC_ASSET_ID, assetIdNode);
+ }
+ node.set(ENTITY_TYPE, new TextNode(serializeEnumName(element.getEntityType().name())));
+ return asValueNode(node);
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode value = valueFromNode("Cannot update entity", idShortPath, valueOnly);
+ JsonNode statementsNode = value.get(STATEMENTS);
+ if(statementsNode == null) {
+ element.getStatements().clear();
+ } else {
+ ElementsListMapper statementsMapper = new ElementsListMapper<>(
+ element, element.getStatements(), idShortPath + "." + STATEMENTS);
+ statementsMapper.update(statementsNode);
+ }
+ JsonNode globalAssetIdNode = value.get(GLOBAL_ASSET_ID);
+ if(globalAssetIdNode == null || globalAssetIdNode.isNull()) {
+ element.setGlobalAssetId(null);
+ } else if(globalAssetIdNode.isTextual()) {
+ element.setGlobalAssetId(globalAssetIdNode.asText());
+ } else {
+ throw new ValueOnlySerializationException("Cannot update the Entity at idShort path '" +
+ idShortPath + "', as the passed " + GLOBAL_ASSET_ID + " is not a string.", idShortPath);
+ }
+ JsonNode specificAssetIdNode = value.get(SPECIFIC_ASSET_ID);
+ if(specificAssetIdNode != null) {
+ if(!specificAssetIdNode.isObject()) {
+ throw new ValueOnlySerializationException("Cannot update the Entity at idShort path '" +
+ idShortPath + "', as the passed " + SPECIFIC_ASSET_ID + " is not an object.", idShortPath);
+ }
+ updateSpecificAssetIds(element.getSpecificAssetIds(), (ObjectNode) specificAssetIdNode);
+ }
+ JsonNode entityTypeNode = value.get(ENTITY_TYPE);
+ if(entityTypeNode == null || !entityTypeNode.isTextual()) {
+ throw new ValueOnlySerializationException("Cannot update the Entity at idShort path '" +
+ idShortPath + "', as its type is not set as string property '" + ENTITY_TYPE + "'.", idShortPath);
+ }
+ element.setEntityType(EntityType.valueOf(deserializeEnumName(entityTypeNode.textValue())));
+ }
+
+ private void updateSpecificAssetIds(List specificAssetIds, ObjectNode objNode)
+ throws ValueOnlySerializationException {
+ for (Iterator it = objNode.fieldNames(); it.hasNext(); ) {
+ String name = it.next();
+ SpecificAssetId specificAssetId = findSpecificAssetIdByName(specificAssetIds, name);
+ JsonNode valueNode = objNode.get(name);
+ if(!valueNode.isTextual()) {
+ throw new ValueOnlySerializationException("Cannot update the SpecificAssetId at IdShort path '" +
+ idShortPath + "." + SPECIFIC_ASSET_ID + "." + name + "' as its value is not set as string property.",
+ idShortPath);
+ }
+ specificAssetId.setValue(valueNode.textValue());
+ }
+ }
+
+ private SpecificAssetId findSpecificAssetIdByName(List specificAssetIds, String name)
+ throws ValueOnlySerializationException {
+ for (SpecificAssetId saId : specificAssetIds) {
+ if(name.equals(saId.getName())) {
+ return saId;
+ }
+ }
+ throw new ValueOnlySerializationException(
+ "Cannot find the SpecificAssetId with name '" + name + "'.", idShortPath + "." + SPECIFIC_ASSET_ID);
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/FileMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/FileMapper.java
new file mode 100644
index 000000000..5f0403de9
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/FileMapper.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.File;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+/**
+ * File is serialized as named JSON object with ${File/idShort} as the name of the containing JSON property. The JSON
+ * object contains two JSON properties. The first refers to the content type named "contentType" and value
+ * ${File/contentType}. The latter refers to the value named “value” and value ${File/value}. The resulting value-only
+ * object is indistinguishable whether it contains File or Blob attributes. Therefore, the receiver needs to take the
+ * type of the target resource into account. Since the receiver knows in advance if a File or a Blob SubmodelElement
+ * shall be manipulated, it can parse the transferred value-only object accordingly as a File or Blob object.
+ */
+class FileMapper extends AbstractMapper {
+ private static final String CONTENT_TYPE = "contentType";
+ private static final String VALUE = "value";
+
+ FileMapper(File file, String idShortPath) {
+ super(file, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ node.set(CONTENT_TYPE, new TextNode(element.getContentType()));
+ node.set(VALUE, new TextNode(element.getValue()));
+ return node;
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode contentNode = valueOnly.get(CONTENT_TYPE);
+ if(contentNode == null || contentNode.isNull()) {
+ element.setContentType(null);
+ } else if(contentNode.isTextual()) {
+ element.setContentType(contentNode.textValue());
+ } else {
+ throw new ValueOnlySerializationException(
+ "Invalid File " + CONTENT_TYPE + " at idShort path '" + idShortPath + "'.", idShortPath);
+ }
+
+ JsonNode valueNode = valueOnly.get(VALUE);
+ if(valueNode == null || valueNode.isNull()) {
+ element.setValue(null);
+ } else if(contentNode != null && contentNode.isTextual()) {
+ element.setValue(valueNode.textValue());
+ } else {
+ throw new ValueOnlySerializationException(
+ "Invalid File " + VALUE + " at idShort path '" + idShortPath + "'.", idShortPath);
+ }
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlyDeserialiser.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlyDeserialiser.java
new file mode 100644
index 000000000..d588fa6a4
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlyDeserialiser.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer;
+import org.eclipse.digitaltwin.aas4j.v3.model.Reference;
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * This class implements the value-only Serialization in JSON format, as described in section 11.4.2 of
+ * AAS Specification Part 2
+ *
+ * Values are only available for:
+ *
+ * - All subtypes of abstract type DataElement
+ * - SubmodelElementList and SubmodelElementCollection respectively for their included SubmodelElements
+ * - ReferenceElement
+ * - RelationshipElement + AnnotatedRelationshipElement
+ * - Entity
+ * - BasicEventElement
+ *
+ */
+public class JsonValueOnlyDeserialiser extends JsonDeserializer {
+
+ JsonNode toJson(Reference reference) {
+ return mapper.valueToTree(reference) ;
+ }
+
+ JsonNode readTree(String jsonString) throws ValueOnlySerializationException {
+ try {
+ return mapper.readTree(jsonString);
+ } catch (JsonProcessingException e) {
+ throw new ValueOnlySerializationException("Cannot parse the value only string: ", e, "$");
+ }
+
+ }
+
+
+ public Reference deserialiseReference(JsonNode refNode, String idShortPath) throws ValueOnlySerializationException {
+ if(refNode == null) {
+ return null;
+ }
+ try {
+ return mapper.treeToValue(refNode, Reference.class);
+ } catch (JsonProcessingException e) {
+ throw new ValueOnlySerializationException(
+ "Cannot deserialize a reference at idShort path + '" + idShortPath + "'.", e, idShortPath);
+ }
+ }
+
+
+
+ /**
+ * The default constructor creates a value-only mapper which serializes and deserializes submodels and submodel
+ * elements to a compact value-only JSON string.
+ */
+ public JsonValueOnlyDeserialiser() {
+
+ }
+
+
+
+ /**
+ * Update an existing submodel with the given value-only JSON string.
+ *
Note:The update is not an atomic operation and if an exception is thrown, the corresponding submodel
+ * will be in an inconsistent state. If you cannot handle such situations, pass a copy of the original submodel.
+ * @param submodel The submodel to be updated. If you want to prevent the direct modification of the original
+ * submodel, just use the corresponding copy constructor, when you pass this argument. Not null.
+ * @param valueOnly the valueOnly string. Not null.
+ *
+ */
+ public void deserialise(Submodel submodel, String valueOnly) throws ValueOnlySerializationException {
+ JsonNode node = readTree(valueOnly);
+ SubmodelMapper mapper = new SubmodelMapper(submodel, "$");
+ mapper.update(node);
+ }
+
+
+ /**
+ * Update an existing submodel element with the given valueOnly.
+ *
Note:The update is not an atomic operation and if an exception is thrown, the corresponding element
+ * will be in an inconsistent state. If you cannot handle such situations, pass a copy of the original element.
+ * @param element The submodel element to be updated. If you want to prevent the direct modification of the original
+ * submodel element, just use the corresponding copy constructor, when you pass this argument.
+ * Not null.
+ * @param valueOnly the valueOnly string. Not null.
+ */
+ public void deserialise(SubmodelElement element, String valueOnly) throws ValueOnlySerializationException {
+ JsonNode node = readTree(valueOnly);
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(element, "$");
+ mapper.update(node);
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlySerialiser.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlySerialiser.java
new file mode 100644
index 000000000..1f9e150ed
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlySerialiser.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonSerializer;
+import org.eclipse.digitaltwin.aas4j.v3.model.Reference;
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * This class implements the value-only Serialization in JSON format, as described in section 11.4.2 of
+ * AAS Specification Part 2
+ *
+ * Values are only available for:
+ *
+ * - All subtypes of abstract type DataElement
+ * - SubmodelElementList and SubmodelElementCollection respectively for their included SubmodelElements
+ * - ReferenceElement
+ * - RelationshipElement + AnnotatedRelationshipElement
+ * - Entity
+ * - BasicEventElement
+ *
+ */
+public class JsonValueOnlySerialiser extends JsonSerializer {
+
+ private final boolean prettyString;
+
+ JsonNode toJson(Reference reference) {
+ return mapper.valueToTree(reference) ;
+ }
+
+ JsonNode readTree(String jsonString) throws ValueOnlySerializationException {
+ try {
+ return mapper.readTree(jsonString);
+ } catch (JsonProcessingException e) {
+ throw new ValueOnlySerializationException("Cannot parse the value only string: ", e, "$");
+ }
+
+ }
+
+ /**
+ * The default constructor creates a value-only mapper which serializes and deserializes submodels and submodel
+ * elements to a compact value-only JSON string.
+ */
+ public JsonValueOnlySerialiser() {
+ this(false);
+ }
+
+ /**
+ * Creates a value-only mapper.
+ * @param prettyString pass true, if you want to have a pretty formatted value-only JSON strings.
+ */
+ public JsonValueOnlySerialiser(boolean prettyString) {
+ this.prettyString = prettyString;
+ }
+
+ /**
+ * Serializes a submodel in value-only JSON format.
+ * @param submodel the submodel to be serialized. Not null.
+ * @return the corresponding value-only JSON string.
+ */
+ public String serialise(Submodel submodel) throws ValueOnlySerializationException {
+ SubmodelMapper mapper = new SubmodelMapper(submodel, "$");
+ JsonNode node = mapper.toJson();
+ return stringify(node);
+ }
+
+ /**
+ * Update an existing submodel with the given value-only JSON string.
+ *
Note:The update is not an atomic operation and if an exception is thrown, the corresponding submodel
+ * will be in an inconsistent state. If you cannot handle such situations, pass a copy of the original submodel.
+ * @param submodel The submodel to be updated. If you want to prevent the direct modification of the original
+ * submodel, just use the corresponding copy constructor, when you pass this argument. Not null.
+ * @param valueOnly the valueOnly string. Not null.
+ *
+ */
+ public void update(Submodel submodel, String valueOnly) throws ValueOnlySerializationException {
+ JsonNode node = readTree(valueOnly);
+ SubmodelMapper mapper = new SubmodelMapper(submodel, "$");
+ mapper.update(node);
+ }
+
+ /**
+ * Serializes a submodel element in value-only JSON format.
+ * @param element the submodel element to be serialized. Not null.
+ * @return the corresponding value-only JSON string.
+ */
+ public String serialise(SubmodelElement element) throws ValueOnlySerializationException {
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(element, "$");
+ if(mapper == null) {
+ throw new ValueOnlySerializationException(
+ "Value-only serialization is not allowed for submodel elements of type '" + element.getClass() + "'.",
+ "$");
+ }
+ JsonNode node = mapper.toJson();
+ return stringify(node);
+ }
+
+ /**
+ * Update an existing submodel element with the given valueOnly.
+ *
Note:The update is not an atomic operation and if an exception is thrown, the corresponding element
+ * will be in an inconsistent state. If you cannot handle such situations, pass a copy of the original element.
+ * @param element The submodel element to be updated. If you want to prevent the direct modification of the original
+ * submodel element, just use the corresponding copy constructor, when you pass this argument.
+ * Not null.
+ * @param valueOnly the valueOnly string. Not null.
+ */
+ public void update(SubmodelElement element, String valueOnly) throws ValueOnlySerializationException {
+ JsonNode node = readTree(valueOnly);
+ ValueOnlyMapper mapper = ValueOnlyMapper.createMapper(element, "$");
+ mapper.update(node);
+ }
+
+ private String stringify(JsonNode node) {
+ return prettyString ? node.toPrettyString() : node.toString();
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/MultiLanguagePropertyMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/MultiLanguagePropertyMapper.java
new file mode 100644
index 000000000..11b978413
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/MultiLanguagePropertyMapper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType;
+import org.eclipse.digitaltwin.aas4j.v3.model.MultiLanguageProperty;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringTextType;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.NullNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+/**
+ * MultiLanguageProperty is serialized as named JSON object with ${MultiLanguageProperty/idShort} as the name of the
+ * containing JSON property. The JSON object contains an array of JSON objects for each language of the
+ * MultiLanguageProperty with the language as name and the corresponding localized string as value of the respective
+ * JSON property. The language name is defined as two chars according to ISO 639-1.
+ */
+class MultiLanguagePropertyMapper extends AbstractMapper {
+
+ MultiLanguagePropertyMapper(MultiLanguageProperty property, String idShortPath) {
+ super(property, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ List langTexts = element.getValue();
+ if(langTexts == null || langTexts.isEmpty()) {
+ return NullNode.instance;
+ }
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ for (LangStringTextType langText: langTexts) {
+ node.set(langText.getLanguage(), new TextNode(langText.getText()));
+ }
+ return node;
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ if(!valueOnly.isObject()) {
+ throw new ValueOnlySerializationException(
+ "Cannot update the multi-language property at idShort path '" + idShortPath +
+ "', as the passed value-only is not a JSON object.", idShortPath);
+ }
+ ObjectNode propNode = (ObjectNode)valueOnly;
+ List value = element.getValue();
+ value.clear();
+ for (Iterator it = propNode.fieldNames(); it.hasNext(); ) {
+ String language = it.next();
+ JsonNode textNode = propNode.get(language);
+ if(!textNode.isTextual()) {
+ String fullPath = idShortPath + "." + language;
+ throw new ValueOnlySerializationException(
+ "Cannot update the multi-language property at idShort path '" + fullPath +
+ "', as the passed value is not a string.", idShortPath);
+ }
+ value.add(new DefaultLangStringTextType.Builder().language(language).text(textNode.textValue()).build());
+ }
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/PropertyMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/PropertyMapper.java
new file mode 100644
index 000000000..c3cacafe7
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/PropertyMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Property;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Property is serialized as ${Property/idShort}: ${Property/value} where ${Property/value} is the JSON serialization
+ * of the respective property’s value in accordance with the data type to value mapping.
+ * @see ValueConverter
+ */
+class PropertyMapper extends AbstractMapper {
+ PropertyMapper(Property property, String idShortPath) {
+ super(property, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ try {
+ JsonNode value = ValueConverter.convert(element.getValueType(), element.getValue());
+ return asValueNode(value);
+ } catch (NumberFormatException ex) {
+ throw new ValueOnlySerializationException("Cannot serialize the property with idShort path '" +
+ idShortPath + "': " + ex.getMessage(), idShortPath);
+ }
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode valueNode = valueFromNode("Cannot update the property", idShortPath, valueOnly);
+ element.setValue(readValueAsString("Cannot update the property", idShortPath, valueNode));
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/RangeMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/RangeMapper.java
new file mode 100644
index 000000000..2d1167d7c
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/RangeMapper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd;
+import org.eclipse.digitaltwin.aas4j.v3.model.Range;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Range is serialized as named JSON object with ${Range/idShort} as the name of the containing JSON property. The JSON
+ * object contains two JSON properties. The first is named "min". The second is named "max". Their corresponding values
+ * are ${Range/min} and ${Range/max}.
+ */
+class RangeMapper extends AbstractMapper {
+ private static final String MIN = "min";
+ private static final String MAX = "max";
+
+ RangeMapper(Range range, String idShortPath) {
+ super(range, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ try {
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ DataTypeDefXsd valueType = element.getValueType();
+ node.set(MIN, ValueConverter.convert(valueType, element.getMin()));
+ node.set(MAX, ValueConverter.convert(valueType, element.getMax()));
+ return asValueNode(node);
+ } catch (NumberFormatException ex) {
+ throw new ValueOnlySerializationException("Cannot serialize the range with idShort path '" +
+ idShortPath + "': " + ex.getMessage(), idShortPath);
+ }
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode valueNode = valueFromNode("Cannot update Range", idShortPath, valueOnly);
+ element.setMax(readValueAsString("Cannot update Range." + MAX, idShortPath, valueNode.get(MAX)));
+ element.setMin(readValueAsString("Cannot update Range." + MIN, idShortPath, valueNode.get(MIN)));
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ReferenceElementMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ReferenceElementMapper.java
new file mode 100644
index 000000000..9dd4df1ec
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ReferenceElementMapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceElement;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * ReferenceElement is serialized as ${ReferenceElement/idShort}: ${ReferenceElement/value} where
+ * ${ReferenceElement/value} is the serialization of the Reference class.
+ */
+class ReferenceElementMapper extends AbstractMapper {
+
+ ReferenceElementMapper(ReferenceElement element, String idShortPath) {
+ super(element, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ JsonNode value = new JsonValueOnlySerialiser().toJson(element.getValue());
+ return asValueNode(value);
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonNode reference = valueFromNode("Cannot update ReferenceElement", idShortPath, valueOnly);
+ element.setValue(new JsonValueOnlyDeserialiser().deserialiseReference(reference, idShortPath));
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/RelationshipElementMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/RelationshipElementMapper.java
new file mode 100644
index 000000000..9646bc5ff
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/RelationshipElementMapper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.RelationshipElement;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * RelationshipElement is serialized as named JSON object with ${RelationshipElement/idShort} as the name of the
+ * containing JSON property. The JSON object contains two JSON properties. The first is named "first". The second is
+ * named "second". Their corresponding values are ${RelationshipElement/first} resp. ${Relationship/second}. The values
+ * are serialized according to the serialization of a Reference.
+ */
+class RelationshipElementMapper extends AbstractMapper {
+ private static final String FIRST = "first";
+ private static final String SECOND = "second";
+
+ RelationshipElementMapper(RelationshipElement relationship, String idShortPath) {
+ super(relationship, idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ ObjectNode node = JsonNodeFactory.instance.objectNode();
+ JsonValueOnlySerialiser serialiser = new JsonValueOnlySerialiser();
+ node.set(FIRST, serialiser.toJson(element.getFirst()));
+ node.set(SECOND, serialiser.toJson(element.getSecond()));
+ return asValueNode(node);
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ JsonValueOnlyDeserialiser deserialiser = new JsonValueOnlyDeserialiser();
+ JsonNode value = valueFromNode("Cannot update the relationship element", idShortPath, valueOnly);
+ element.setFirst(deserialiser.deserialiseReference(value.get(FIRST), idShortPath + "." + FIRST));
+ element.setSecond(deserialiser.deserialiseReference(value.get(SECOND), idShortPath + "." + SECOND));
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/SubmodelMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/SubmodelMapper.java
new file mode 100644
index 000000000..277015229
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/SubmodelMapper.java
@@ -0,0 +1,23 @@
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * A submodel is serialized as an unnamed JSON object.
+ */
+public class SubmodelMapper extends AbstractCollectionMapper {
+ SubmodelMapper(Submodel submodel, String idShortPath) {
+ super(submodel, submodel.getSubmodelElements(), idShortPath);
+ }
+
+ @Override
+ public JsonNode toJson() throws ValueOnlySerializationException {
+ return valuesToJson();
+ }
+
+ @Override
+ public void update(JsonNode valueOnly) throws ValueOnlySerializationException {
+ updateFromJson(valueOnly);
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueConverter.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueConverter.java
new file mode 100644
index 000000000..b215d78b4
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueConverter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.DoubleNode;
+import com.fasterxml.jackson.databind.node.FloatNode;
+import com.fasterxml.jackson.databind.node.IntNode;
+import com.fasterxml.jackson.databind.node.LongNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd;
+
+import java.math.BigDecimal;
+
+/**
+ * This is a helper class for the serialization of string values depending of their data type. The data types are
+ * defined according to the W3C XML Schema (https://www.w3.org/TR/xmlschema-2/#built-in-datatypes and
+ * https://www.w3.org/TR/xmlschema-2/#built-in-derived).
+ * Note that for xs:decimal, xs:unsignedLong, xs:positiveInteger, xs:nonNegativeInteger, xs:negativeInteger and
+ * xs:nonPositiveInteger exists the possibility for lost of precision during the conversion.
+ */
+class ValueConverter {
+ private ValueConverter() {}
+
+ /**
+ *
+ * @param dataType The data type.
+ * @param value
+ * @return
+ * @throws NumberFormatException
+ */
+ static JsonNode convert(DataTypeDefXsd dataType, String value) throws NumberFormatException {
+ if(dataType == null) {
+ return new TextNode(value);
+ }
+
+ switch (dataType) {
+ case BOOLEAN:
+ return Boolean.parseBoolean(value) ? BooleanNode.TRUE : BooleanNode.FALSE;
+ case DECIMAL:
+ // According to the AAS spec, this type is serialized as a number.
+ // There is a possibility for lost of precision.
+ return new DoubleNode(new BigDecimal(value).doubleValue());
+ case BYTE:
+ case UNSIGNED_BYTE:
+ case SHORT:
+ case UNSIGNED_SHORT:
+ case INT:
+ return new IntNode(Integer.parseInt(value));
+ case UNSIGNED_INT:
+ case POSITIVE_INTEGER:
+ case NEGATIVE_INTEGER:
+ case NON_NEGATIVE_INTEGER:
+ case NON_POSITIVE_INTEGER:
+ case LONG:
+ return new LongNode(Long.parseLong(value));
+ case UNSIGNED_LONG:
+ // According to the spec, it should be serialized as number. There is a possibility for precision lost.
+ case DOUBLE:
+ return new DoubleNode(Double.parseDouble(value));
+ case FLOAT:
+ return new FloatNode(Float.parseFloat(value));
+ default:
+ // All other types have no need to be converted from string.
+ return new TextNode(value);
+ }
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueOnlyMapper.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueOnlyMapper.java
new file mode 100644
index 000000000..061944b91
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueOnlyMapper.java
@@ -0,0 +1,86 @@
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AnnotatedRelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.BasicEventElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.Blob;
+import org.eclipse.digitaltwin.aas4j.v3.model.Entity;
+import org.eclipse.digitaltwin.aas4j.v3.model.File;
+import org.eclipse.digitaltwin.aas4j.v3.model.MultiLanguageProperty;
+import org.eclipse.digitaltwin.aas4j.v3.model.Property;
+import org.eclipse.digitaltwin.aas4j.v3.model.Range;
+import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.RelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList;
+import com.fasterxml.jackson.databind.JsonNode;
+
+public interface ValueOnlyMapper {
+
+ /**
+ * This method converts the corresponding element to a value-only JSON node.
+ *
+ * @return the corresponding JSON node.
+ * @throws ValueOnlySerializationException with information about the idShort path.
+ */
+ JsonNode toJson() throws ValueOnlySerializationException;
+
+ /**
+ * Updates the corresponding element according the passed valueOnly JSON node.
+ *
+ * @param valueOnly the value only JSON node.
+ * @throws ValueOnlySerializationException with information about the idShort path.
+ *
Note:The update is not an atomic operation and if an exception is thrown, the corresponding element
+ * will be in an inconsistent state. If you cannot handle such situations, pass a copy of the original element.
+ */
+ void update(JsonNode valueOnly) throws ValueOnlySerializationException;
+
+ /**
+ * Creates the corresponding mapper.
+ *
+ * @param element the submodel element.
+ * @param idShortPath the idShort path.
+ * @return the corresponding mapper or null if this type cannot be serialized to value-only JSON string.
+ */
+ static ValueOnlyMapper createMapper(SubmodelElement element, String idShortPath) {
+ if (element instanceof Blob) {
+ return new BlobMapper((Blob) element, idShortPath);
+ }
+ if (element instanceof File) {
+ return new FileMapper((File) element, idShortPath);
+ }
+ if (element instanceof MultiLanguageProperty) {
+ return new MultiLanguagePropertyMapper((MultiLanguageProperty) element, idShortPath);
+ }
+ if (element instanceof Property) {
+ return new PropertyMapper((Property) element, idShortPath);
+ }
+ if (element instanceof Range) {
+ return new RangeMapper((Range) element, idShortPath);
+ }
+ if (element instanceof ReferenceElement) {
+ return new ReferenceElementMapper((ReferenceElement) element, idShortPath);
+ }
+ if (element instanceof Entity) {
+ return new EntityMapper((Entity) element, idShortPath);
+ }
+ if (element instanceof BasicEventElement) {
+ return new BasicEventElementMapper((BasicEventElement) element, idShortPath);
+ }
+ if (element instanceof SubmodelElementCollection) {
+ SubmodelElementCollection elementCollection = (SubmodelElementCollection) element;
+ return new ElementsCollectionMapper(elementCollection, elementCollection.getValue(), idShortPath);
+ }
+ if (element instanceof SubmodelElementList) {
+ SubmodelElementList elementList = (SubmodelElementList) element;
+ return new ElementsListMapper<>(elementList, elementList.getValue(), idShortPath);
+ }
+ if (element instanceof AnnotatedRelationshipElement) {
+ return new AnnotatedRelationshipMapper((AnnotatedRelationshipElement) element, idShortPath);
+ }
+ if (element instanceof RelationshipElement) {
+ return new RelationshipElementMapper((RelationshipElement) element, idShortPath);
+ }
+ return null;
+ }
+}
diff --git a/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueOnlySerializationException.java b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueOnlySerializationException.java
new file mode 100644
index 000000000..6222dec1a
--- /dev/null
+++ b/dataformat-json/src/main/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/ValueOnlySerializationException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+/**
+ * This exception is thrown during the value-only serialization or deserialization.
+ */
+public class ValueOnlySerializationException extends RuntimeException {
+ private final String idShortPath;
+
+ /**
+ * The constructor.
+ * @param msg The exception message
+ * @param idShortPath the idShort path is a dot separated chain of idShorts, that can be used in case of
+ * troubleshooting.
+ */
+ public ValueOnlySerializationException(String msg, String idShortPath) {
+ super(msg);
+ this.idShortPath = idShortPath;
+ }
+
+ /**
+ * The constructor.
+ * @param msg The exception message.
+ * @param cause The root cause.
+ * @param idShortPath the idShort path is a dot separated chain of idShorts, that can be used in case of
+ * troubleshooting.
+ */
+ public ValueOnlySerializationException(String msg, Throwable cause, String idShortPath) {
+ super(msg, cause);
+ this.idShortPath = idShortPath;
+ }
+
+ /**
+ * Return the corresponding idShort path.
+ * @return the idShort path is a dot separated chain of idShorts, that can be used in case of troubleshooting.
+ */
+ public String getIdShortPath() {
+ return idShortPath;
+ }
+}
diff --git a/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlyDeserialiserTest.java b/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlyDeserialiserTest.java
new file mode 100644
index 000000000..09cffa276
--- /dev/null
+++ b/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlyDeserialiserTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AnnotatedRelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.Blob;
+import org.eclipse.digitaltwin.aas4j.v3.model.Entity;
+import org.eclipse.digitaltwin.aas4j.v3.model.File;
+import org.eclipse.digitaltwin.aas4j.v3.model.MultiLanguageProperty;
+import org.eclipse.digitaltwin.aas4j.v3.model.Property;
+import org.eclipse.digitaltwin.aas4j.v3.model.Range;
+import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import static org.junit.Assert.assertEquals;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class JsonValueOnlyDeserialiserTest {
+
+ private static final JsonValueOnlyDeserialiser deserialiser = new JsonValueOnlyDeserialiser();
+ private static final JsonValueOnlySerialiser serialiser = new JsonValueOnlySerialiser();
+
+ @Test
+ public void testUpdateSubmodel() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.SUBMODEL_UPDATED);
+ Submodel actual = TestData.SUBMODEL.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.SUBMODEL_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateEntity() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.ENTITY_UPDATED);
+ Entity actual = TestData.ENTITY.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.ENTITY_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateProperty() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_INT_UPDATED);
+ Property actual = TestData.PROPERTY_INT.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.PROPERTY_INT_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateRange() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.RANGE_DOUBLE_UPDATED);
+ Range actual = TestData.RANGE_DOUBLE.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.RANGE_DOUBLE_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateBlob() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.BLOB_UPDATED);
+ Blob actual = TestData.BLOB.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.BLOB_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateFile() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.FILE_UPDATED);
+ File actual = TestData.FILE.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.FILE_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateMultiLangProperty() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.MULTI_LANGUAGE_PROPERTY_UPDATED);
+ MultiLanguageProperty actual = TestData.MULTI_LANGUAGE_PROPERTY.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.MULTI_LANGUAGE_PROPERTY_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdatePropertyDouble() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_DOUBLE_UPDATED);
+ Property actual = TestData.PROPERTY_DOUBLE.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.PROPERTY_DOUBLE_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdatePropertyDatetime() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_DATETIME_UPDATED);
+ Property actual = TestData.PROPERTY_DATETIME.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.PROPERTY_DATETIME_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdatePropertyString() {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_STRING_UPDATED);
+ Property actual = TestData.PROPERTY_STRING.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.PROPERTY_STRING_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateRefElementGlobal() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.REFERENCE_ELEMENT_GLOBAL_UPDATED);
+ ReferenceElement actual = TestData.REFERENCE_ELEMENT_GLOBAL.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.REFERENCE_ELEMENT_GLOBAL_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateAnnotatedRelationshipElement() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.ANNOTATED_RELATIONSHIP_ELEMENT_UPDATED);
+ AnnotatedRelationshipElement actual = TestData.ANNOTATED_RELATIONSHIP_ELEMENT.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.ANNOTATED_RELATIONSHIP_ELEMENT_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateCollectionElement() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.ELEMENT_COLLECTION_UPDATED);
+ SubmodelElementCollection actual = TestData.ELEMENT_COLLECTION.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.ELEMENT_COLLECTION_UPDATED, actual);
+ }
+
+ @Test
+ public void testUpdateListElement() throws ValueOnlySerializationException {
+ String valueOnly = serialiser.serialise(TestData.ELEMENT_LIST_UPDATED);
+ SubmodelElementList actual = TestData.ELEMENT_LIST.get();
+ deserialiser.deserialise(actual, valueOnly);
+ assertEquals(TestData.ELEMENT_LIST_UPDATED, actual);
+ }
+
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlySerialiserTest.java b/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlySerialiserTest.java
new file mode 100644
index 000000000..bf4c9cf2e
--- /dev/null
+++ b/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/JsonValueOnlySerialiserTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.json.JSONException;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class JsonValueOnlySerialiserTest {
+ private static final JsonValueOnlySerialiser serialiser = new JsonValueOnlySerialiser(true);
+
+
+ @Test
+ public void testSerializeSubmodel() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.SUBMODEL.get());
+ String expected = readValueOnlyFile("submodel.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeEntity() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.ENTITY.get());
+ String expected = readValueOnlyFile("entity.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeIntProperty() throws ValueOnlySerializationException, IOException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_INT.get());
+ String expected = readValueOnlyFile("property_int.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeRange() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.RANGE_DOUBLE.get());
+ String expected = readValueOnlyFile("range.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeBlob() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.BLOB.get());
+ String expected = readValueOnlyFile("blob.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeFile() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.FILE.get());
+ String expected = readValueOnlyFile("file.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeMultiLangProperty() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.MULTI_LANGUAGE_PROPERTY.get());
+ String expected = readValueOnlyFile("multi_lang_property.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializePropertyDouble() throws ValueOnlySerializationException, JSONException, IOException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_DOUBLE.get());
+ String expected = readValueOnlyFile("property_double.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializePropertyDatetime() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_DATETIME.get());
+ String expected = readValueOnlyFile("date_time_property.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializePropertyString() throws IOException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.PROPERTY_STRING.get());
+ String expected = readValueOnlyFile("property_string.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeRefElementGlobal() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.REFERENCE_ELEMENT_GLOBAL.get());
+ String expected = readValueOnlyFile("ref_element_global.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeAnnotatedRelationshipElement() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.ANNOTATED_RELATIONSHIP_ELEMENT.get());
+ String expected = readValueOnlyFile("ann_relationship_element.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+ @Test
+ public void testSerializeCollectionElement() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.ELEMENT_COLLECTION.get());
+ String expected = readValueOnlyFile("element_collection.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ // failing
+ }
+
+ @Test
+ public void testSerializeListElement() throws IOException, ValueOnlySerializationException, JSONException {
+ String valueOnly = serialiser.serialise(TestData.ELEMENT_LIST.get());
+ String expected = readValueOnlyFile("element_list.json");
+ JSONAssert.assertEquals(expected, valueOnly, JSONCompareMode.NON_EXTENSIBLE);
+ }
+
+
+ private String readValueOnlyFile(String valueOnlyFile) throws IOException {
+ return new String(getClass().getClassLoader().getResourceAsStream(
+ "valueonly/" + valueOnlyFile).readAllBytes(), StandardCharsets.UTF_8);
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/TestData.java b/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/TestData.java
new file mode 100644
index 000000000..dd0446a88
--- /dev/null
+++ b/dataformat-json/src/test/java/org/eclipse/digitaltwin/aas4j/v3/dataformat/json/valueonly/TestData.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2023 SAP SE or an SAP affiliate company.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.digitaltwin.aas4j.v3.dataformat.json.valueonly;
+
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.function.Supplier;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AnnotatedRelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.Blob;
+import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd;
+import org.eclipse.digitaltwin.aas4j.v3.model.Entity;
+import org.eclipse.digitaltwin.aas4j.v3.model.EntityType;
+import org.eclipse.digitaltwin.aas4j.v3.model.File;
+import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes;
+import org.eclipse.digitaltwin.aas4j.v3.model.ModellingKind;
+import org.eclipse.digitaltwin.aas4j.v3.model.MultiLanguageProperty;
+import org.eclipse.digitaltwin.aas4j.v3.model.Property;
+import org.eclipse.digitaltwin.aas4j.v3.model.Range;
+import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes;
+import org.eclipse.digitaltwin.aas4j.v3.model.RelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection;
+import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAnnotatedRelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultBlob;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEntity;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultFile;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultKey;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringTextType;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultMultiLanguageProperty;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperation;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultRange;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReferenceElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultRelationshipElement;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList;
+
+public class TestData {
+ public static final Supplier ENTITY = () -> new DefaultEntity.Builder()
+ .idShort("entity1")
+ .entityType(EntityType.SELF_MANAGED_ENTITY)
+ .globalAssetId("Global Asset Id")
+ .statements(new DefaultProperty.Builder()
+ .idShort("maxRotationSpeed")
+ .valueType(DataTypeDefXsd.INT)
+ .value("5000")
+ .build())
+ .build();
+
+ public static final Entity ENTITY_UPDATED = new DefaultEntity.Builder()
+ .idShort("entity1")
+ .entityType(EntityType.CO_MANAGED_ENTITY)
+ .globalAssetId("Global Asset Id Updated")
+ .statements(new DefaultProperty.Builder()
+ .idShort("maxRotationSpeed")
+ .valueType(DataTypeDefXsd.INT)
+ .value("5001")
+ .build())
+ .build();
+
+ public static final Supplier PROPERTY_STRING = () -> new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propString")
+ .value("foo")
+ .build();
+
+ public static final Property PROPERTY_STRING_UPDATED = new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propString")
+ .value("foo updated")
+ .build();
+
+ public static final Supplier RANGE_DOUBLE = () -> new DefaultRange.Builder()
+ .idShort("rangeDouble")
+ .valueType(DataTypeDefXsd.DOUBLE)
+ .min("3.0")
+ .max("5.0")
+ .build();
+
+ public static final Range RANGE_DOUBLE_UPDATED = new DefaultRange.Builder()
+ .idShort("rangeDouble")
+ .valueType(DataTypeDefXsd.DOUBLE)
+ .min("3.0")
+ .max("5.0")
+ .build();
+
+ public static final Supplier BLOB = () -> new DefaultBlob.Builder()
+ .idShort("blob1")
+ .contentType("application/octet-stream")
+ .value("example-data".getBytes())
+ .build();
+
+ public static final Blob BLOB_UPDATED = new DefaultBlob.Builder()
+ .idShort("blob1")
+ .contentType("application/json")
+ .value("{ value: 42 }".getBytes())
+ .build();
+
+ public static final Supplier FILE = () -> new DefaultFile.Builder()
+ .idShort("file1")
+ .contentType("application/pdf")
+ .value("SafetyInstructions.pdf")
+ .build();
+
+ public static final File FILE_UPDATED = new DefaultFile.Builder()
+ .idShort("file1")
+ .contentType("application/json")
+ .value("SafetyInstructions.json")
+ .build();
+
+ public static final Supplier MULTI_LANGUAGE_PROPERTY = () -> new DefaultMultiLanguageProperty.Builder()
+ .idShort("multiLanguageProp1")
+ .value(new DefaultLangStringTextType.Builder().text("foo").language("de").build())
+ .value(new DefaultLangStringTextType.Builder() .text("bar").language("en").build())
+ .build();
+
+ public static final MultiLanguageProperty MULTI_LANGUAGE_PROPERTY_UPDATED = new DefaultMultiLanguageProperty.Builder()
+ .idShort("multiLanguageProp1")
+ .value(new DefaultLangStringTextType.Builder().text("foo updated").language("fr").build())
+ .value(new DefaultLangStringTextType.Builder() .text("bar updated").language("de").build())
+ .build();
+
+ public static final Supplier PROPERTY_DOUBLE = () -> new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propDouble")
+ .valueType(DataTypeDefXsd.DOUBLE)
+ .value("42.17")
+ .build();
+
+ public static final Property PROPERTY_DOUBLE_UPDATED = new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propDouble")
+ .valueType(DataTypeDefXsd.DOUBLE)
+ .value("24.71")
+ .build();
+
+ public static final Supplier PROPERTY_DATETIME = () -> new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propDateTime")
+ .valueType(DataTypeDefXsd.DATE_TIME)
+ .value(ZonedDateTime.of(2022, 7, 31, 17, 8, 51, 0, ZoneOffset.UTC).toString())
+ .build();
+
+ public static final Property PROPERTY_DATETIME_UPDATED = new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propDateTime")
+ .valueType(DataTypeDefXsd.DATE_TIME)
+ .value(ZonedDateTime.of(2023, 7, 31, 17, 8, 51, 0, ZoneOffset.UTC).toString())
+ .build();
+
+ public static final Supplier PROPERTY_INT = () -> new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propInt")
+ .valueType(DataTypeDefXsd.INT)
+ .value("42")
+ .build();
+
+ public static final Property PROPERTY_INT_UPDATED = new DefaultProperty.Builder()
+ .category("category")
+ .idShort("propInt")
+ .valueType(DataTypeDefXsd.INT)
+ .value("24")
+ .build();
+
+ public static final Supplier RANGE_INT = () -> new DefaultRange.Builder()
+ .idShort("rangeInt")
+ .valueType(DataTypeDefXsd.INT)
+ .min("17")
+ .max("42")
+ .build();
+
+ public static final Range RANGE_INT_UPDATED = new DefaultRange.Builder()
+ .idShort("rangeInt")
+ .valueType(DataTypeDefXsd.INT)
+ .min("24")
+ .max("50")
+ .build();
+
+ public static final Supplier REFERENCE_ELEMENT_GLOBAL = () -> new DefaultReferenceElement.Builder()
+ .idShort("referenceGlobal")
+ .value(new DefaultReference.Builder().type(ReferenceTypes.EXTERNAL_REFERENCE)
+ .referredSemanticId(new DefaultReference.Builder()
+ .type(ReferenceTypes.EXTERNAL_REFERENCE)
+ .keys(new DefaultKey.Builder()
+ .type(KeyTypes.CONCEPT_DESCRIPTION)
+ .value("Concept Description key value")
+ .build())
+ .build())
+ .keys(new DefaultKey.Builder()
+ .type(KeyTypes.GLOBAL_REFERENCE)
+ .value("global reference key value")
+ .build())
+ .build())
+ .build();
+
+ public static final ReferenceElement REFERENCE_ELEMENT_GLOBAL_UPDATED = new DefaultReferenceElement.Builder()
+ .idShort("referenceGlobal")
+ .value(new DefaultReference.Builder().type(ReferenceTypes.EXTERNAL_REFERENCE)
+ .referredSemanticId(new DefaultReference.Builder()
+ .type(ReferenceTypes.EXTERNAL_REFERENCE)
+ .keys(new DefaultKey.Builder()
+ .type(KeyTypes.GLOBAL_REFERENCE)
+ .value("Global reference key value updated")
+ .build())
+ .build())
+ .keys(new DefaultKey.Builder()
+ .type(KeyTypes.FILE)
+ .value("file key value")
+ .build())
+ .build())
+ .build();
+
+ public static final Supplier REFERENCE_ELEMENT_MODEL = () -> new DefaultReferenceElement.Builder()
+ .idShort("referenceModel")
+ .value(new DefaultReference.Builder()
+ .type(ReferenceTypes.MODEL_REFERENCE)
+ .keys(new DefaultKey.Builder()
+ .type(KeyTypes.PROPERTY)
+ .value("MaxRotationSpeed")
+ .build())
+ .build())
+ .build();
+
+ public static final ReferenceElement REFERENCE_ELEMENT_MODEL_UPDATED = new DefaultReferenceElement.Builder()
+ .idShort("referenceModel")
+ .value(new DefaultReference.Builder()
+ .type(ReferenceTypes.EXTERNAL_REFERENCE)
+ .keys(new DefaultKey.Builder()
+ .type(KeyTypes.GLOBAL_REFERENCE)
+ .value("Global reference key value updated")
+ .build())
+ .build())
+ .build();
+
+ public static final Supplier ANNOTATED_RELATIONSHIP_ELEMENT = () -> new DefaultAnnotatedRelationshipElement.Builder()
+ .idShort("annotatedRelationship1")
+ .first(REFERENCE_ELEMENT_GLOBAL.get().getValue())
+ .second(REFERENCE_ELEMENT_MODEL.get().getValue())
+ .annotations(PROPERTY_DATETIME.get())
+ .annotations(RANGE_INT.get())
+ .build();
+
+ public static final AnnotatedRelationshipElement ANNOTATED_RELATIONSHIP_ELEMENT_UPDATED = new DefaultAnnotatedRelationshipElement.Builder()
+ .idShort("annotatedRelationship1")
+ .first(REFERENCE_ELEMENT_GLOBAL_UPDATED.getValue())
+ .second(REFERENCE_ELEMENT_MODEL_UPDATED.getValue())
+ .annotations(PROPERTY_DATETIME_UPDATED)
+ .annotations(RANGE_INT_UPDATED)
+ .build();
+
+ public static final Supplier RELATIONSHIP_ELEMENT = () -> new DefaultRelationshipElement.Builder()
+ .idShort("relationship1")
+ .first(REFERENCE_ELEMENT_GLOBAL.get().getValue())
+ .second(REFERENCE_ELEMENT_MODEL.get().getValue())
+ .build();
+
+
+ public static final RelationshipElement RELATIONSHIP_ELEMENT_UPDATED = new DefaultRelationshipElement.Builder()
+ .idShort("relationship1")
+ .first(REFERENCE_ELEMENT_GLOBAL_UPDATED.getValue())
+ .second(REFERENCE_ELEMENT_MODEL_UPDATED.getValue())
+ .build();
+
+ public static final Supplier ELEMENT_COLLECTION = () -> new DefaultSubmodelElementCollection.Builder()
+ .idShort("collection1")
+ .value(PROPERTY_STRING.get())
+ .value(RANGE_DOUBLE.get())
+ .value(ENTITY.get())
+ .value(RELATIONSHIP_ELEMENT.get())
+ .build();
+
+ public static final SubmodelElementCollection ELEMENT_COLLECTION_UPDATED = new DefaultSubmodelElementCollection.Builder()
+ .idShort("collection1")
+ .value(PROPERTY_STRING_UPDATED)
+ .value(RANGE_DOUBLE_UPDATED)
+ .value(ENTITY_UPDATED)
+ .value(RELATIONSHIP_ELEMENT_UPDATED)
+ .build();
+
+ public static final Supplier ELEMENT_LIST = () -> new DefaultSubmodelElementList.Builder()
+ .idShort("list1")
+ .value(PROPERTY_STRING.get())
+ .value(RANGE_DOUBLE.get())
+ .value(ENTITY.get())
+ .value(ANNOTATED_RELATIONSHIP_ELEMENT.get())
+ .build();
+
+ public static final SubmodelElementList ELEMENT_LIST_UPDATED = new DefaultSubmodelElementList.Builder()
+ .idShort("list1")
+ .value(PROPERTY_STRING_UPDATED)
+ .value(RANGE_DOUBLE_UPDATED)
+ .value(ENTITY_UPDATED)
+ .value(ANNOTATED_RELATIONSHIP_ELEMENT_UPDATED)
+ .build();
+
+ public static final Supplier SUBMODEL = () -> new DefaultSubmodel.Builder()
+ .category("category")
+ .idShort("submodel1")
+ .kind(ModellingKind.INSTANCE)
+ .submodelElements(PROPERTY_STRING.get())
+ .submodelElements(RANGE_DOUBLE.get())
+ .submodelElements(ELEMENT_COLLECTION.get())
+ .submodelElements(new DefaultOperation.Builder()
+ .idShort("operation1")
+ .build())
+ .build();
+
+ public static final Submodel SUBMODEL_UPDATED = new DefaultSubmodel.Builder()
+ .category("category")
+ .idShort("submodel1")
+ .kind(ModellingKind.INSTANCE)
+ .submodelElements(PROPERTY_STRING_UPDATED)
+ .submodelElements(RANGE_DOUBLE_UPDATED)
+ .submodelElements(ELEMENT_COLLECTION_UPDATED)
+ .submodelElements(new DefaultOperation.Builder()
+ .idShort("operation1")
+ .build())
+ .build();
+}
diff --git a/dataformat-json/src/test/resources/valueonly/ann_relationship_element.json b/dataformat-json/src/test/resources/valueonly/ann_relationship_element.json
new file mode 100644
index 000000000..e0ebd5764
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/ann_relationship_element.json
@@ -0,0 +1,42 @@
+{
+ "annotatedRelationship1": {
+ "first": {
+ "referredSemanticId": {
+ "keys": [
+ {
+ "type": "ConceptDescription",
+ "value": "Concept Description key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "keys": [
+ {
+ "type": "GlobalReference",
+ "value": "global reference key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "second": {
+ "keys": [
+ {
+ "type": "Property",
+ "value": "MaxRotationSpeed"
+ }
+ ],
+ "type": "ModelReference"
+ },
+ "annotations": [
+ {
+ "propDateTime": "2022-07-31T17:08:51Z"
+ },
+ {
+ "rangeInt": {
+ "min": 17,
+ "max": 42
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/blob.json b/dataformat-json/src/test/resources/valueonly/blob.json
new file mode 100644
index 000000000..c47688e6f
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/blob.json
@@ -0,0 +1,4 @@
+{
+ "contentType" : "application/octet-stream",
+ "value" : "ZXhhbXBsZS1kYXRh"
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/date_time_property.json b/dataformat-json/src/test/resources/valueonly/date_time_property.json
new file mode 100644
index 000000000..eb26358e0
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/date_time_property.json
@@ -0,0 +1,3 @@
+{
+ "propDateTime": "2022-07-31T17:08:51Z"
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/element_collection.json b/dataformat-json/src/test/resources/valueonly/element_collection.json
new file mode 100644
index 000000000..ef42617c3
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/element_collection.json
@@ -0,0 +1,47 @@
+{
+ "collection1": {
+ "propString": "foo",
+ "rangeDouble": {
+ "min": 3.0,
+ "max": 5.0
+ },
+ "entity1": {
+ "statements": [
+ {
+ "maxRotationSpeed": 5000
+ }
+ ],
+ "globalAssetId": "Global Asset Id",
+ "entityType": "SelfManagedEntity"
+ },
+ "relationship1": {
+ "first": {
+ "referredSemanticId": {
+ "keys": [
+ {
+ "type": "ConceptDescription",
+ "value": "Concept Description key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "keys": [
+ {
+ "type": "GlobalReference",
+ "value": "global reference key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "second": {
+ "keys": [
+ {
+ "type": "Property",
+ "value": "MaxRotationSpeed"
+ }
+ ],
+ "type": "ModelReference"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/element_list.json b/dataformat-json/src/test/resources/valueonly/element_list.json
new file mode 100644
index 000000000..6d338fab2
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/element_list.json
@@ -0,0 +1,64 @@
+[
+ {
+ "propString": "foo"
+ },
+ {
+ "rangeDouble": {
+ "min": 3.0,
+ "max": 5.0
+ }
+ },
+ {
+ "entity1": {
+ "statements": [
+ {
+ "maxRotationSpeed": 5000
+ }
+ ],
+ "globalAssetId": "Global Asset Id",
+ "entityType": "SelfManagedEntity"
+ }
+ },
+ {
+ "annotatedRelationship1": {
+ "first": {
+ "referredSemanticId": {
+ "keys": [
+ {
+ "type": "ConceptDescription",
+ "value": "Concept Description key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "keys": [
+ {
+ "type": "GlobalReference",
+ "value": "global reference key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "second": {
+ "keys": [
+ {
+ "type": "Property",
+ "value": "MaxRotationSpeed"
+ }
+ ],
+ "type": "ModelReference"
+ },
+ "annotations": [
+ {
+ "propDateTime": "2022-07-31T17:08:51Z"
+ },
+ {
+ "rangeInt": {
+ "min": 17,
+ "max": 42
+ }
+ }
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/entity.json b/dataformat-json/src/test/resources/valueonly/entity.json
new file mode 100644
index 000000000..a5b8435a1
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/entity.json
@@ -0,0 +1,11 @@
+{
+ "entity1": {
+ "statements": [
+ {
+ "maxRotationSpeed": 5000
+ }
+ ],
+ "globalAssetId": "Global Asset Id",
+ "entityType": "SelfManagedEntity"
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/file.json b/dataformat-json/src/test/resources/valueonly/file.json
new file mode 100644
index 000000000..fa87bdd03
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/file.json
@@ -0,0 +1,4 @@
+{
+ "contentType" : "application/pdf",
+ "value" : "SafetyInstructions.pdf"
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/multi_lang_property.json b/dataformat-json/src/test/resources/valueonly/multi_lang_property.json
new file mode 100644
index 000000000..b39b3e33f
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/multi_lang_property.json
@@ -0,0 +1,4 @@
+{
+ "de" : "foo",
+ "en" : "bar"
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/property_double.json b/dataformat-json/src/test/resources/valueonly/property_double.json
new file mode 100644
index 000000000..c97edc2f4
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/property_double.json
@@ -0,0 +1,3 @@
+{
+ "propDouble": 42.17
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/property_int.json b/dataformat-json/src/test/resources/valueonly/property_int.json
new file mode 100644
index 000000000..74e12fe45
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/property_int.json
@@ -0,0 +1,3 @@
+{
+ "propInt": 42
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/property_string.json b/dataformat-json/src/test/resources/valueonly/property_string.json
new file mode 100644
index 000000000..d7448dce7
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/property_string.json
@@ -0,0 +1,3 @@
+{
+ "propString": "foo"
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/range.json b/dataformat-json/src/test/resources/valueonly/range.json
new file mode 100644
index 000000000..90131d233
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/range.json
@@ -0,0 +1,6 @@
+{
+ "rangeDouble": {
+ "min": 3.0,
+ "max": 5.0
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/ref_element_global.json b/dataformat-json/src/test/resources/valueonly/ref_element_global.json
new file mode 100644
index 000000000..05c86e833
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/ref_element_global.json
@@ -0,0 +1,20 @@
+{
+ "referenceGlobal": {
+ "referredSemanticId": {
+ "keys": [
+ {
+ "type": "ConceptDescription",
+ "value": "Concept Description key value"
+ }
+ ],
+ "type": "ExternalReference"
+ },
+ "keys": [
+ {
+ "type": "GlobalReference",
+ "value": "global reference key value"
+ }
+ ],
+ "type": "ExternalReference"
+ }
+}
\ No newline at end of file
diff --git a/dataformat-json/src/test/resources/valueonly/submodel.json b/dataformat-json/src/test/resources/valueonly/submodel.json
new file mode 100644
index 000000000..94fb51efc
--- /dev/null
+++ b/dataformat-json/src/test/resources/valueonly/submodel.json
@@ -0,0 +1,46 @@
+{
+ "propString" : "foo",
+ "rangeDouble" : {
+ "min" : 3.0,
+ "max" : 5.0
+ },
+ "collection1" : {
+ "propString" : "foo",
+ "rangeDouble" : {
+ "min" : 3.0,
+ "max" : 5.0
+ },
+ "entity1" : {
+ "statements": [
+ {
+ "maxRotationSpeed": 5000
+ }
+ ],
+ "globalAssetId": "Global Asset Id",
+ "entityType": "SelfManagedEntity"
+ },
+ "relationship1" : {
+ "first" : {
+ "referredSemanticId" : {
+ "keys" : [ {
+ "type" : "ConceptDescription",
+ "value" : "Concept Description key value"
+ } ],
+ "type" : "ExternalReference"
+ },
+ "keys" : [ {
+ "type" : "GlobalReference",
+ "value" : "global reference key value"
+ } ],
+ "type" : "ExternalReference"
+ },
+ "second" : {
+ "keys" : [ {
+ "type" : "Property",
+ "value" : "MaxRotationSpeed"
+ } ],
+ "type" : "ModelReference"
+ }
+ }
+ }
+}
\ No newline at end of file