Skip to content

Commit

Permalink
Add support for Serialization / deserialization of POJO
Browse files Browse the repository at this point in the history
  • Loading branch information
vrichard12 committed Jan 29, 2024
1 parent 72ac65d commit 17dc1c4
Show file tree
Hide file tree
Showing 14 changed files with 422 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
*******************************************************************************/
package org.eclipse.sirius.emfjson.resource;

import com.google.gson.Gson;
import com.google.gson.JsonElement;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
Expand Down Expand Up @@ -425,6 +428,12 @@ interface IEObjectHandler {
*/
Object OPTION_SAVE_FEATURES_ORDER_COMPARATOR = "OPTION_SAVE_FEATURES_ORDER_COMPARATOR"; //$NON-NLS-1$

/**
* Specify a {@link Function} returning {@link Boolean#TRUE} if a given {@link EDataType} should be serialized
* to/from Json with {@link Gson}, {@link Boolean#FALSE} otherwise.
*/
String OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER = "isEDataTypeSerializableInJsonTester"; //$NON-NLS-1$

/**
* Associate an ID to the {@link EObject}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.sirius.emfjson.utils;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
Expand All @@ -31,6 +32,7 @@
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
Expand Down Expand Up @@ -910,21 +912,47 @@ private EObject loadEGenericType(JsonObject jsonObject) {
private void deserializeEAttribute(EAttribute eAttribute, JsonElement jsonElement, EObject eObject) {
EDataType dataType = eAttribute.getEAttributeType();
if (!eAttribute.isMany()) {
String newValue = this.getAsFlexibleString(jsonElement);
Object value = this.tryCreateDataTypeFromString(dataType, newValue);
Object value = null;
if (this.isEDataTypeSerializableInJson(dataType)) {
value = new Gson().fromJson(jsonElement, eAttribute.getEType().getInstanceClass());
} else {
String newValue = this.getAsFlexibleString(jsonElement);
value = this.tryCreateDataTypeFromString(dataType, newValue);
}
this.helper.setValue(eObject, eAttribute, value);
} else {
JsonArray asJsonArray = this.getAsFlexibleArray(jsonElement);
Object eGet = this.helper.getValue(eObject, eAttribute);
if (eGet instanceof Collection<?>) {
for (JsonElement jElement : asJsonArray) {
Object value = this.tryCreateDataTypeFromString(dataType, jElement.getAsString());
Object value = null;
if (this.isEDataTypeSerializableInJson(dataType)) {
value = new Gson().fromJson(jElement, eAttribute.getEType().getInstanceClass());
} else {
value = this.tryCreateDataTypeFromString(dataType, jElement.getAsString());
}
this.helper.setValue(eObject, eAttribute, value);
}
}
}
}

/**
* Test the given {@link EDataType} with the {@link JsonResource#OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER}
* option. If the option is not set, return false.
*
* @param eDataType
* @return true if the given {@link EDataType} should be deserialized from a Json tree.
*/
private boolean isEDataTypeSerializableInJson(EDataType eDataType) {
@SuppressWarnings("unchecked")
Function<EDataType, Boolean> isEDataTypeJsonSerializableTester = (Function<EDataType, Boolean>) this.options.get(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER);
if (isEDataTypeJsonSerializableTester != null) {
return isEDataTypeJsonSerializableTester.apply(eDataType).booleanValue();
}
return false;
}

/**
* Try to create a {@link EDataType} instance from a serialized form of the value. If the serialization is not
* compatible with the given EDataType, returns the serialized form of the value unchanged.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
*******************************************************************************/
package org.eclipse.sirius.emfjson.utils;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
Expand All @@ -31,6 +33,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.EList;
Expand Down Expand Up @@ -1040,22 +1043,48 @@ private JsonElement serializeEDataType(EObject eObject, EAttribute eAttribute) {
if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
for (Object object : collection) {
jsonArray.add(new JsonPrimitive(eFactoryInstance.convertToString(eAttribute.getEAttributeType(), object)));
if (object == null) {
jsonArray.add(JsonNull.INSTANCE);
} else if (this.isEDataTypeSerializableInJson(eAttribute.getEAttributeType())) {
jsonArray.add(new Gson().toJsonTree(object));
} else {
jsonArray.add(new JsonPrimitive(eFactoryInstance.convertToString(eAttribute.getEAttributeType(), object)));
}
}
}
jsonElement = jsonArray;
} else {
String stringValue = eFactoryInstance.convertToString(eAttribute.getEAttributeType(), value);

if (stringValue == null) {
stringValue = ""; //$NON-NLS-1$
if (this.isEDataTypeSerializableInJson(eAttribute.getEAttributeType())) {
jsonElement = new Gson().toJsonTree(value);
} else {
if (stringValue == null) {
jsonElement = JsonNull.INSTANCE;
} else {
jsonElement = new JsonPrimitive(stringValue);
}
}
jsonElement = new JsonPrimitive(stringValue);
}

return jsonElement;
}

/**
* Test the given {@link EDataType} with the {@link JsonResource#OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER}
* option. If the option is not set, return false.
*
* @param eDataType
* @return true if the given {@link EDataType} should be serialized as a Json tree.
*/
private boolean isEDataTypeSerializableInJson(EDataType eDataType) {
@SuppressWarnings("unchecked")
Function<EDataType, Boolean> isEDataTypeJsonSerializableTester = (Function<EDataType, Boolean>) this.options.get(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER);
if (isEDataTypeJsonSerializableTester != null) {
return isEDataTypeJsonSerializableTester.apply(eDataType).booleanValue();
}
return false;
}

/**
* Returns the JsonElement representing an EByteArray.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@

package org.eclipse.sirius.emfjson.tests.internal.unit.load;

import java.util.function.Function;

import org.eclipse.emf.ecore.EDataType;
import org.eclipse.sirius.emfjson.resource.JsonResource;
import org.eclipse.sirius.emfjson.tests.internal.AbstractEMFJsonTests;
import org.junit.Test;

import model.TestPojoDataTypeImpl;

/**
* Tests class for EDataType deserialization.
*
Expand Down Expand Up @@ -49,4 +55,36 @@ public void testMultipleSerialiationDataType() {
this.testLoad("NodeMultipleCustomDataType.xmi"); //$NON-NLS-1$
}

/**
* Test deserialization of POJO EDataType EAttribute monovalued.
*/
@Test
public void testLoadSingleValueAttributePojoDataType() {
Function<EDataType, Boolean> eDataTypeJsonSerializableTester = new Function<EDataType, Boolean>() {
@Override
public Boolean apply(EDataType eDataType) {
return Boolean.valueOf(eDataType.getInstanceClass() == TestPojoDataTypeImpl.class);
}
};
this.options.put(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER, eDataTypeJsonSerializableTester);

this.testLoad("NodeSingleValueAttributePojoDataType.xmi"); //$NON-NLS-1$
}

/**
* Test deserialization of POJO EDataType EAttribute multivalued.
*/
@Test
public void testLoadMultiValuedAttributePojoDataType() {
Function<EDataType, Boolean> eDataTypeJsonSerializableTester = new Function<EDataType, Boolean>() {
@Override
public Boolean apply(EDataType eDataType) {
return Boolean.valueOf(eDataType.getInstanceClass() == TestPojoDataTypeImpl.class);
}
};
this.options.put(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER, eDataTypeJsonSerializableTester);

this.testLoad("NodeMultiValuedAttributePojoDataType.xmi"); //$NON-NLS-1$
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public EStructuralFeature getElement(EClass eClass, String namespace, String nam
// $NON-NLS-1$ //$NON-NLS-2$
return eClass.getEStructuralFeature("singleValuedReference"); //$NON-NLS-1$
}
// return super.getElement(eClass, namespace, name); // Doesn't work
// return super.getElement(eClass, namespace, name); // Doesn't work because the super implementation in
// Ecore ends up checking that the string is XMI
return eClass.getEStructuralFeature(name);
}
};
Expand All @@ -65,7 +66,8 @@ public EStructuralFeature getElement(EClass eClass, String namespace, String nam
if ("NodeMultiValuedAttribute".equals(eClass.getName()) && "multiStringAttributeOld".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
return eClass.getEStructuralFeature("multiStringAttribute"); //$NON-NLS-1$
}
// return super.getElement(eClass, namespace, name); // Doesn't work
// return super.getElement(eClass, namespace, name); // Doesn't work because the super implementation in
// Ecore ends up checking that the string is XMI
return eClass.getEStructuralFeature(name);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ public EStructuralFeature getElement(EClass eClass, String namespace, String nam
if ("NodeSingleValueAttribute".equals(eClass.getName()) && "singleStringAttributeOld".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
return eClass.getEStructuralFeature("singleStringAttribute"); //$NON-NLS-1$
}
return super.getElement(eClass, namespace, name);
// return super.getElement(eClass, namespace, name); // Doesn't work because the super implementation in
// Ecore ends up checking that the string is XMI
return eClass.getEStructuralFeature(name);
}

};
Expand All @@ -71,7 +73,9 @@ public EStructuralFeature getElement(EClass eClass, String namespace, String nam
if ("NodeMultiValuedAttribute".equals(eClass.getName()) && "multiStringAttributeOld".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
return eClass.getEStructuralFeature("multiStringAttribute"); //$NON-NLS-1$
}
return super.getElement(eClass, namespace, name);
// return super.getElement(eClass, namespace, name); // Doesn't work because the super implementation in
// Ecore ends up checking that the string is XMI
return eClass.getEStructuralFeature(name);
}

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@
*******************************************************************************/
package org.eclipse.sirius.emfjson.tests.internal.unit.load;

import java.util.function.Function;

import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.sirius.emfjson.resource.JsonResource;
import org.eclipse.sirius.emfjson.tests.internal.AbstractEMFJsonTests;
import org.eclipse.sirius.emfjson.utils.JsonHelper;
import org.junit.Test;

import model.TestPojoDataTypeImpl;

/**
* Tests loading with ExtendedMetaData.
*/
Expand All @@ -42,13 +47,22 @@ public void testChangeAttributeTypeMono() {
public void setValue(EObject object, EStructuralFeature feature, Object value) {
Object newValue = value;
if ("NodeSingleValueAttribute".equals(feature.getEContainingClass().getName()) && "singleIntAttribute".equals(feature.getName())) { //$NON-NLS-1$ //$NON-NLS-2$
newValue = new Integer(((String) value).length());
newValue = Integer.valueOf(((String) value).length());
}
super.setValue(object, feature, newValue);
}
};

this.options.put(JsonResource.OPTION_CUSTOM_HELPER, jsonHelper);

Function<EDataType, Boolean> eDataTypeJsonSerializableTester = new Function<EDataType, Boolean>() {
@Override
public Boolean apply(EDataType eDataType) {
return Boolean.valueOf(eDataType.getInstanceClass() == TestPojoDataTypeImpl.class);
}
};
this.options.put(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER, eDataTypeJsonSerializableTester);

this.testLoad("TestChangeAttributeTypeMono.xmi"); //$NON-NLS-1$
}

Expand All @@ -62,7 +76,7 @@ public void testChangeAttributeTypeMulti() {
public void setValue(EObject object, EStructuralFeature feature, Object value) {
Object newValue = value;
if ("NodeMultiValuedAttribute".equals(feature.getEContainingClass().getName()) && "multiIntAttribute".equals(feature.getName())) { //$NON-NLS-1$ //$NON-NLS-2$
newValue = new Integer(((String) value).length());
newValue = Integer.valueOf(((String) value).length());
}
super.setValue(object, feature, newValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@

package org.eclipse.sirius.emfjson.tests.internal.unit.save;

import java.util.function.Function;

import org.eclipse.emf.ecore.EDataType;
import org.eclipse.sirius.emfjson.resource.JsonResource;
import org.eclipse.sirius.emfjson.tests.internal.AbstractEMFJsonTests;
import org.junit.Test;

import model.TestPojoDataTypeImpl;

/**
* Tests class for EDataType serialization.
*
Expand Down Expand Up @@ -49,4 +55,36 @@ public void testMultipleSerialiationDataType() {
this.testSave("NodeMultipleCustomDataType.xmi"); //$NON-NLS-1$
}

/**
* Test serialization of POJO EDataType EAttribute monovalued.
*/
@Test
public void testSaveSingleValueAttributePojoDataType() {
Function<EDataType, Boolean> eDataTypeJsonSerializableTester = new Function<EDataType, Boolean>() {
@Override
public Boolean apply(EDataType eDataType) {
return Boolean.valueOf(eDataType.getInstanceClass() == TestPojoDataTypeImpl.class);
}
};
this.options.put(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER, eDataTypeJsonSerializableTester);

this.testSave("NodeSingleValueAttributePojoDataType.xmi"); //$NON-NLS-1$
}

/**
* Test serialization of POJO EDataType EAttribute multivalued.
*/
@Test
public void testSaveMultiValuedAttributePojoDataType() {
Function<EDataType, Boolean> eDataTypeJsonSerializableTester = new Function<EDataType, Boolean>() {
@Override
public Boolean apply(EDataType eDataType) {
return Boolean.valueOf(eDataType.getInstanceClass() == TestPojoDataTypeImpl.class);
}
};
this.options.put(JsonResource.OPTION_IS_EDATATYPE_SERIALIZABLE_IN_JSON_TESTER, eDataTypeJsonSerializableTester);

this.testSave("NodeMultiValuedAttributePojoDataType.xmi"); //$NON-NLS-1$
}

}
Loading

0 comments on commit 17dc1c4

Please sign in to comment.