2727import com .google .gson .JsonObject ;
2828import com .microsoft .graph .logger .ILogger ;
2929
30+ import java .util .HashMap ;
31+ import java .util .Iterator ;
3032import java .util .Map ;
3133
3234/**
@@ -55,7 +57,7 @@ public DefaultSerializer(final ILogger logger) {
5557 }
5658
5759 /**
58- * Deserialize an object from the input string.
60+ * Deserializes an object from the input string.
5961 *
6062 * @param inputString The string that stores the representation of the item.
6163 * @param clazz The .class of the item to be deserialized.
@@ -66,7 +68,7 @@ public DefaultSerializer(final ILogger logger) {
6668 public <T > T deserializeObject (final String inputString , final Class <T > clazz ) {
6769 final T jsonObject = gson .fromJson (inputString , clazz );
6870
69- // Populate the json backed fields for any annotations that are not in the object model
71+ // Populate the JSON- backed fields for any annotations that are not in the object model
7072 if (jsonObject instanceof IJsonBackedObject ) {
7173 logger .logDebug ("Deserializing type " + clazz .getSimpleName ());
7274 final IJsonBackedObject jsonBackedObject = (IJsonBackedObject ) jsonObject ;
@@ -93,27 +95,99 @@ public <T> String serializeObject(final T serializableObject) {
9395 JsonElement outJsonTree = gson .toJsonTree (serializableObject );
9496
9597 if (serializableObject instanceof IJsonBackedObject ) {
96- AdditionalDataManager additionalData =
97- ((IJsonBackedObject ) serializableObject )
98- .additionalDataManager ();
98+ IJsonBackedObject serializableJsonObject = (IJsonBackedObject ) serializableObject ;
99+
100+ AdditionalDataManager additionalData = serializableJsonObject .additionalDataManager ();
101+
102+ // If the item is a valid Graph object, add its additional data
99103 if (outJsonTree .isJsonObject ()) {
100104 JsonObject outJson = outJsonTree .getAsJsonObject ();
101- for (Map .Entry <String , JsonElement > entry : additionalData .entrySet ()) {
102- if (!fieldIsOdataTransient (entry )) {
103- outJson .add (
104- entry .getKey (),
105- entry .getValue ()
106- );
107- }
108- }
105+
106+ addAdditionalDataToJson (additionalData , outJson );
107+ outJson = getChildAdditionalData (serializableJsonObject , outJson );
108+
109109 outJsonTree = outJson ;
110110 }
111111 }
112112
113113 return outJsonTree .toString ();
114114 }
115+
116+ /**
117+ * Recursively populates additional data for each child object
118+ *
119+ * @param serializableObject The child to get additional data for
120+ * @param outJson The serialized output JSON to add to
121+ * @return The serialized output JSON including the additional child data
122+ */
123+ private JsonObject getChildAdditionalData (IJsonBackedObject serializableObject , JsonObject outJson ) {
124+ // Use reflection to iterate through fields for eligible Graph children
125+ for (java .lang .reflect .Field field : serializableObject .getClass ().getFields ()) {
126+ try {
127+ Object fieldObject = field .get (serializableObject );
128+
129+ // If the object is a HashMap, iterate through its children
130+ if (fieldObject instanceof HashMap ) {
131+ HashMap serializableChildren = (HashMap ) fieldObject ;
132+ Iterator it = serializableChildren .entrySet ().iterator ();
133+
134+ while (it .hasNext ()) {
135+ HashMap .Entry pair = (HashMap .Entry )it .next ();
136+ Object child = pair .getValue ();
137+
138+ // If the item is a valid Graph object, add its additional data
139+ if (child instanceof IJsonBackedObject ) {
140+ AdditionalDataManager childAdditionalData = ((IJsonBackedObject ) child ).additionalDataManager ();
141+
142+ addAdditionalDataToJson (
143+ childAdditionalData ,
144+ outJson .getAsJsonObject (
145+ field .getName ())
146+ .getAsJsonObject ()
147+ .get (pair .getKey ()
148+ .toString ())
149+ .getAsJsonObject ());
150+
151+ // Serialize its children
152+ outJson = getChildAdditionalData ((IJsonBackedObject )child , outJson );
153+ }
154+ }
155+ }
156+ // If the object is a valid Graph object, add its additional data
157+ else if (fieldObject != null && fieldObject instanceof IJsonBackedObject ) {
158+ IJsonBackedObject serializableChild = (IJsonBackedObject ) fieldObject ;
159+ AdditionalDataManager childAdditionalData = serializableChild .additionalDataManager ();
160+
161+ addAdditionalDataToJson (childAdditionalData , outJson .get (field .getName ()).getAsJsonObject ());
162+
163+ // Serialize its children
164+ outJson = getChildAdditionalData (serializableChild , outJson );
165+ }
166+ } catch (IllegalArgumentException | IllegalAccessException e ) {
167+ logger .logError ("Unable to access child fields of " + serializableObject .getClass ().getSimpleName (), e );
168+ }
169+ }
170+
171+ return outJson ;
172+ }
173+
174+ /**
175+ * Add each non-transient additional data property to the given JSON node
176+ * @param additionalDataManager The additional data bag to iterate through
177+ * @param jsonNode The JSON node to add the additional data properties to
178+ */
179+ private void addAdditionalDataToJson (AdditionalDataManager additionalDataManager , JsonObject jsonNode ) {
180+ for (Map .Entry <String , JsonElement > entry : additionalDataManager .entrySet ()) {
181+ if (!fieldIsOdataTransient (entry )) {
182+ jsonNode .add (
183+ entry .getKey (),
184+ entry .getValue ()
185+ );
186+ }
187+ }
188+ }
115189
116190 private boolean fieldIsOdataTransient (Map .Entry <String , JsonElement > entry ) {
117- return entry .getKey ().startsWith ("@" );
191+ return ( entry .getKey ().startsWith ("@" ) && entry . getKey () != "@odata.type " );
118192 }
119193}
0 commit comments