Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 58 additions & 44 deletions src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,59 +118,73 @@ public <T> T deserializeObject(final String inputString, final Class<T> clazz, M
* @param serializedObject the parent object whose children will be iterated to set additional data
* @param rawJson the raw json
*/
@SuppressWarnings("unchecked")
private void setChildAdditionalData(IJsonBackedObject serializedObject, JsonObject rawJson) {
private void setChildAdditionalData(final IJsonBackedObject serializedObject, final JsonObject rawJson) {
// Use reflection to iterate through fields for eligible Graph children
for (java.lang.reflect.Field field : serializedObject.getClass().getFields()) {
try {
Object fieldObject = field.get(serializedObject);

// If the object is a HashMap, iterate through its children
if (fieldObject instanceof HashMap) {
HashMap<String, Object> serializableChildren = (HashMap<String, Object>) fieldObject;
Iterator<Entry<String, Object>> it = serializableChildren.entrySet().iterator();
if(rawJson != null) {
for (java.lang.reflect.Field field : serializedObject.getClass().getFields()) {
try {
if(field != null) {
final Object fieldObject = field.get(serializedObject);
if (fieldObject instanceof HashMap) {
// If the object is a HashMap, iterate through its children
@SuppressWarnings("unchecked")
final HashMap<String, Object> serializableChildren = (HashMap<String, Object>) fieldObject;
final Iterator<Entry<String, Object>> it = serializableChildren.entrySet().iterator();

while (it.hasNext()) {
Map.Entry<String, Object> pair = (Map.Entry<String, Object>)it.next();
Object child = pair.getValue();
while (it.hasNext()) {
final Map.Entry<String, Object> pair = (Map.Entry<String, Object>)it.next();
final Object child = pair.getValue();

// If the item is a valid Graph object, set its additional data
if (child instanceof IJsonBackedObject) {
AdditionalDataManager childAdditionalDataManager = ((IJsonBackedObject) child).additionalDataManager();
if(rawJson != null && field != null && rawJson.get(field.getName()) != null && rawJson.get(field.getName()).isJsonObject()
&& rawJson.get(field.getName()).getAsJsonObject().get(pair.getKey()).isJsonObject()) {
childAdditionalDataManager.setAdditionalData(rawJson.get(field.getName()).getAsJsonObject().get(pair.getKey()).getAsJsonObject());
setChildAdditionalData((IJsonBackedObject) child,rawJson.get(field.getName()).getAsJsonObject().get(pair.getKey()).getAsJsonObject());
// If the item is a valid Graph object, set its additional data
if (child instanceof IJsonBackedObject) {
Copy link
Collaborator

@MIchaelMainer MIchaelMainer Sep 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see a test in case where the child is a OData primitive, do we add non-schematized primitive fields to additionalData?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tweaked the last unit test I added so it tests for that as well.

final AdditionalDataManager childAdditionalDataManager = ((IJsonBackedObject) child).additionalDataManager();
final JsonElement fieldElement = rawJson.get(field.getName());
if(fieldElement != null && fieldElement.isJsonObject()
&& fieldElement.getAsJsonObject().get(pair.getKey()) != null
&& fieldElement.getAsJsonObject().get(pair.getKey()).isJsonObject()) {
childAdditionalDataManager.setAdditionalData(fieldElement.getAsJsonObject().get(pair.getKey()).getAsJsonObject());
setChildAdditionalData((IJsonBackedObject) child,fieldElement.getAsJsonObject().get(pair.getKey()).getAsJsonObject());
}
}
}
}
}
}
// If the object is a list of Graph objects, iterate through elements
else if (fieldObject instanceof List && rawJson != null) {
final JsonElement collectionJson = rawJson.get(field.getName());
final List<?> fieldObjectList = (List<?>) fieldObject;
if (collectionJson.isJsonArray() && ((JsonArray)collectionJson).size() == fieldObjectList.size()) {
final JsonArray rawJsonArray = (JsonArray) collectionJson;
for (int i = 0; i < fieldObjectList.size(); i++) {
final Object element = fieldObjectList.get(i);
if (element instanceof IJsonBackedObject) {
final JsonElement elementRawJson = rawJsonArray.get(i);
setChildAdditionalData((IJsonBackedObject) element, elementRawJson.getAsJsonObject());
// If the object is a list of Graph objects, iterate through elements
else if (fieldObject instanceof List) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for fieldObject instanceof HashMap using serializer.deserializeObject(sourcestring, class);

One test for a homogenous list of objects, and one for a hetergenous list of objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a unit test. We currently don't have a case where hashmaps have heterogeneous values and I didn't want to add too much fake code in the test setup.

final JsonElement collectionJson = rawJson.get(field.getName());
final List<?> fieldObjectList = (List<?>) fieldObject;
if (collectionJson != null && collectionJson.isJsonArray()) {
final JsonArray rawJsonArray = (JsonArray) collectionJson;
final Integer fieldObjectListSize = fieldObjectList.size();
final Integer rawJsonArraySize = rawJsonArray.size();
for (int i = 0; i < fieldObjectListSize && i < rawJsonArraySize; i++) {
final Object element = fieldObjectList.get(i);
if (element instanceof IJsonBackedObject) {
final JsonElement elementRawJson = rawJsonArray.get(i);
if(elementRawJson != null) {
setChildAdditionalData((IJsonBackedObject) element, elementRawJson.getAsJsonObject());
}
}
}
if (rawJsonArraySize != fieldObjectListSize)
logger.logDebug("rawJsonArray has a size of " + rawJsonArraySize + " and fieldObjectList of " + fieldObjectListSize);
}
}
// If the object is a valid Graph object, set its additional data
else if (fieldObject instanceof IJsonBackedObject) {
final IJsonBackedObject serializedChild = (IJsonBackedObject) fieldObject;
final AdditionalDataManager childAdditionalDataManager = serializedChild.additionalDataManager();
final JsonElement fieldElement = rawJson.get(field.getName());
if(fieldElement != null && fieldElement.isJsonObject()) {
childAdditionalDataManager.setAdditionalData(fieldElement.getAsJsonObject());
setChildAdditionalData((IJsonBackedObject) fieldObject,fieldElement.getAsJsonObject());
}
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
//Not throwing the IllegalArgumentException as the Serialized Object would still be usable even if the additional data is not set.
logger.logError("Unable to set child fields of " + serializedObject.getClass().getSimpleName(), e);
logger.logDebug(rawJson.getAsString());
}
// If the object is a valid Graph object, set its additional data
else if (fieldObject != null && fieldObject instanceof IJsonBackedObject) {
IJsonBackedObject serializedChild = (IJsonBackedObject) fieldObject;
AdditionalDataManager childAdditionalDataManager = serializedChild.additionalDataManager();
if(rawJson != null && field != null && rawJson.get(field.getName()) != null && rawJson.get(field.getName()).isJsonObject()) {
childAdditionalDataManager.setAdditionalData(rawJson.get(field.getName()).getAsJsonObject());
setChildAdditionalData((IJsonBackedObject) fieldObject,rawJson.get(field.getName()).getAsJsonObject());
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
logger.logError("Unable to access child fields of " + serializedObject.getClass().getSimpleName(), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.microsoft.graph.serializer;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -63,7 +64,20 @@ public void testChildAdditionalData() {
assertEquals("{\"manager\":{\"id\":\"1\",\"additionalData\":\"additionalValue\"},\"id\":\"2\"}", serializedObject);
}

@Test
@Test
public void testSkipTransientData() {
Entity entity = new Entity();
entity.id = "1";

entity.additionalDataManager().put("@odata.type", new JsonPrimitive("entity"));
entity.additionalDataManager().put("@odata.nextLink", new JsonPrimitive("1"));

String serializedObject = serializer.serializeObject(entity);

assertEquals("{\"id\":\"1\",\"@odata.nextLink\":\"1\",\"@odata.type\":\"entity\"}", serializedObject);
}

@Test
public void testPropsAdditionalDataOnNonIJSONObjects() {
final DriveItemUploadableProperties upProps = new DriveItemUploadableProperties();
upProps.name = "vacation.gif";
Expand Down Expand Up @@ -125,4 +139,15 @@ public void testChildAdditionalDataDeserialization() {

assertEquals("\"petertest@onmicrosoft.com\"",email.toString());
}

@Test
public void testHashMapProperties() {
final String source = "{\"description\": \"Task details properties:\nchecklist:Sub items\nreferences:Related links\",\"previewType\": \"automatic\",\"references\": {\"https%3A//developer%2Emicrosoft%2Ecom/en-us/graph/graph-explorer\": {\"@odata.type\": \"#microsoft.graph.plannerExternalReference\",\"alias\": \"Graph Explorer\",\"type\": \"Other\",\"previewPriority\": \"0009005706180391122\",\"lastModifiedBy\": {\"user\": {\"id\": \"fbab97d0-4932-4511-b675-204639209557\"}},\"lastModifiedDateTime\": \"2017-04-24T22:52:29.814Z\"}},\"checklist\": {\"d280ed1a-9f6b-4f9c-a962-fb4d00dc50ff\": {\"@odata.type\": \"#microsoft.graph.plannerChecklistItem\",\"isChecked\": false,\"title\": \"Try reading task details\",\"orderHint\": \"8587094707721254251P]\",\"lastModifiedBy\": {\"user\": {\"id\": \"e396de0e-4812-4fcb-9f9e-0358744df343\", \"customProp\": \"somestring\"}},\"lastModifiedDateTime\": \"2017-04-14T02:16:14.866Z\"}},\"id\": \"gcrYAaAkgU2EQUvpkNNXLGQAGTtu\"}";
final PlannerTaskDetails taskDetails = serializer.deserializeObject(source, PlannerTaskDetails.class);
assertNotNull(taskDetails);
assertNotNull(taskDetails.checklist);
assertFalse(taskDetails.checklist.isEmpty());
assertTrue(taskDetails.checklist.get("d280ed1a-9f6b-4f9c-a962-fb4d00dc50ff").title.equals("Try reading task details"));
assertTrue(taskDetails.checklist.get("d280ed1a-9f6b-4f9c-a962-fb4d00dc50ff").lastModifiedBy.user.additionalDataManager().get("customProp").getAsString().equals("somestring"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public MockSerializer(final Object deserializeReturn, final String serializeRetu
}

@Override
@SuppressWarnings("unchecked")
public <T> T deserializeObject(final String inputString, final Class<T> clazz) {
//noinspection unchecked
return (T) mDeserializeReturn;
Expand All @@ -61,8 +62,8 @@ public <T> String serializeObject(final T serializableObject) {
}

@Override
@SuppressWarnings("unchecked")
public <T> T deserializeObject(String inputString, Class<T> clazz, Map<String, List<String>> responseHeaders) {
// TODO Auto-generated method stub
return (T) mDeserializeReturn;
}
}