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 31, 2024
1 parent 72ac65d commit 4eb40c1
Show file tree
Hide file tree
Showing 14 changed files with 417 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.Predicate;

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 Predicate} evaluating to true if a given {@link EDataType} should be serialized to/from Json
* with {@link Gson}.
*/
String OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE = "shouldEDataTypeBeSerializedInJsonPredicate"; //$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.Predicate;

import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
Expand Down Expand Up @@ -910,21 +912,46 @@ 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.shouldEDataTypeBeSerializedInJson(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.shouldEDataTypeBeSerializedInJson(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} against the
* {@link JsonResource#OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE} option. If the option is not set,
* return false.
*
* @param eDataType
* The {@link EDataType} to test.
* @return true if the given {@link EDataType} should be deserialized from a Json tree.
*/
private boolean shouldEDataTypeBeSerializedInJson(EDataType eDataType) {
@SuppressWarnings("unchecked")
Predicate<EDataType> shouldEDataTypeBeSerializedInJsonPredicate = (Predicate<EDataType>) this.options.get(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE);
return shouldEDataTypeBeSerializedInJsonPredicate != null && shouldEDataTypeBeSerializedInJsonPredicate.test(eDataType);
}

/**
* 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.Predicate;
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.EList;
Expand Down Expand Up @@ -1040,22 +1043,45 @@ 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.shouldEDataTypeBeSerializedInJson(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$
jsonElement = JsonNull.INSTANCE;
} else if (this.shouldEDataTypeBeSerializedInJson(eAttribute.getEAttributeType())) {
jsonElement = new Gson().toJsonTree(value);
} else {
jsonElement = new JsonPrimitive(stringValue);
}
jsonElement = new JsonPrimitive(stringValue);
}

return jsonElement;
}

/**
* Test the given {@link EDataType} against the
* {@link JsonResource#OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE} option. If the option is not set,
* return false.
*
* @param eDataType
* The {@link EDataType} to test.
* @return true if the given {@link EDataType} should be deserialized as a Json tree.
*/
private boolean shouldEDataTypeBeSerializedInJson(EDataType eDataType) {
@SuppressWarnings("unchecked")
Predicate<EDataType> shouldEDataTypeBeSerializedInJsonPredicate = (Predicate<EDataType>) this.options.get(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE);
return shouldEDataTypeBeSerializedInJsonPredicate != null && shouldEDataTypeBeSerializedInJsonPredicate.test(eDataType);
}

/**
* 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.Predicate;

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() {
Predicate<EDataType> eDataTypeJsonSerializableTester = new Predicate<EDataType>() {
@Override
public boolean test(EDataType eDataType) {
return eDataType.getInstanceClass() == TestPojoDataTypeImpl.class;
}
};
this.options.put(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE, eDataTypeJsonSerializableTester);

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

/**
* Test deserialization of POJO EDataType EAttribute multivalued.
*/
@Test
public void testLoadMultiValuedAttributePojoDataType() {
Predicate<EDataType> eDataTypeJsonSerializableTester = new Predicate<EDataType>() {
@Override
public boolean test(EDataType eDataType) {
return eDataType.getInstanceClass() == TestPojoDataTypeImpl.class;
}
};
this.options.put(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE, 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.Predicate;

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,21 @@ 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);

Predicate<EDataType> eDataTypeJsonSerializableTester = new Predicate<EDataType>() {
@Override
public boolean test(EDataType eDataType) {
return eDataType.getInstanceClass() == TestPojoDataTypeImpl.class;
}
};
this.options.put(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE, eDataTypeJsonSerializableTester);

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

Expand All @@ -62,7 +75,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.Predicate;

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() {
Predicate<EDataType> eDataTypeJsonSerializableTester = new Predicate<EDataType>() {
@Override
public boolean test(EDataType eDataType) {
return eDataType.getInstanceClass() == TestPojoDataTypeImpl.class;
}
};
this.options.put(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE, eDataTypeJsonSerializableTester);

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

/**
* Test serialization of POJO EDataType EAttribute multivalued.
*/
@Test
public void testSaveMultiValuedAttributePojoDataType() {
Predicate<EDataType> eDataTypeJsonSerializableTester = new Predicate<EDataType>() {
@Override
public boolean test(EDataType eDataType) {
return eDataType.getInstanceClass() == TestPojoDataTypeImpl.class;
}
};
this.options.put(JsonResource.OPTION_SHOULD_EDATATYPE_BE_SERIALIZED_IN_JSON_PREDICATE, eDataTypeJsonSerializableTester);

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

}
Loading

0 comments on commit 4eb40c1

Please sign in to comment.