Skip to content

Commit a4003cf

Browse files
authored
Merge pull request #483 from microsoftgraph/bugfix/collection-deserialization
- fixes a bug where collections would not be deserialized properly
2 parents 54355c8 + 05928f8 commit a4003cf

File tree

6 files changed

+326
-237
lines changed

6 files changed

+326
-237
lines changed

src/main/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializer.java

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) 2017 Microsoft Corporation
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
8+
// copies of the Software, and to permit persons to whom the Software is
9+
// furnished to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in
12+
// all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
// THE SOFTWARE.
21+
// ------------------------------------------------------------------------------
22+
23+
package com.microsoft.graph.serializer;
24+
25+
import java.lang.reflect.InvocationTargetException;
26+
import java.lang.reflect.Type;
27+
import java.util.ArrayList;
28+
import java.util.Arrays;
29+
import java.util.List;
30+
31+
import com.google.gson.JsonArray;
32+
import com.google.gson.JsonElement;
33+
import com.google.gson.JsonObject;
34+
import com.google.gson.JsonParseException;
35+
import com.google.gson.JsonParser;
36+
import com.google.common.reflect.TypeToken;
37+
import com.microsoft.graph.http.BaseCollectionPage;
38+
import com.microsoft.graph.http.IRequestBuilder;
39+
import com.microsoft.graph.logger.ILogger;
40+
41+
// keep these imports: even though they are not used, it's a good way to call
42+
// for maintainer's attention en the event code generation naming conventions
43+
// change. If the name change, these import will build at build time rather than
44+
// reflection building at run time.
45+
import com.microsoft.graph.models.extensions.Attachment;
46+
import com.microsoft.graph.requests.extensions.AttachmentCollectionPage;
47+
import com.microsoft.graph.requests.extensions.AttachmentCollectionResponse;
48+
import com.microsoft.graph.requests.extensions.IAttachmentCollectionRequestBuilder;
49+
50+
public class CollectionPageSerializer {
51+
52+
private static DefaultSerializer serializer;
53+
/** length of the word "page" */
54+
private final static Integer pageLength = 4;
55+
/** length of the word "collection" */
56+
private final static Integer collectionLength = 10;
57+
/** length of the work "response" */
58+
private final static Integer responseLength = 8;
59+
/** the extensions segment in the package name of target classes */
60+
private final static String extensionsPath = "extensions.";
61+
62+
/**
63+
* Not available for instantiation
64+
*/
65+
private CollectionPageSerializer() {
66+
}
67+
68+
/**
69+
* Serializes an CollectionPage
70+
*
71+
* @param src the CollectionPage variable for serialization
72+
* @param logger the logger
73+
* @return JsonElement of CollectionPage
74+
*/
75+
public static <T1, T2 extends IRequestBuilder> JsonElement serialize(final BaseCollectionPage<T1, T2> src, final ILogger logger) {
76+
if(src == null) {
77+
return null;
78+
}
79+
JsonArray jsonArray = new JsonArray();
80+
List<T1> items = src.getCurrentPage();
81+
serializer = new DefaultSerializer(logger);
82+
for(T1 item : items) {
83+
final String json = serializer.serializeObject(item);
84+
final JsonElement element = JsonParser.parseString(json);
85+
if(element != null && element.isJsonObject()) {
86+
final JsonObject jsonObject = element.getAsJsonObject();
87+
jsonArray.add(jsonObject);
88+
}
89+
}
90+
return jsonArray;
91+
}
92+
93+
/**
94+
* Deserializes the JsonElement
95+
*
96+
* @param json the source CollectionPage's Json
97+
* @param typeOfT The type of the CollectionPage to deserialize to
98+
* @param logger the logger
99+
* @throws JsonParseException the parse exception
100+
* @return the deserialized CollectionPage
101+
*/
102+
@SuppressWarnings("unchecked")
103+
public static <T1, T2 extends IRequestBuilder> BaseCollectionPage<T1, T2> deserialize(final JsonElement json, Type typeOfT, final ILogger logger) throws JsonParseException {
104+
if (json == null) {
105+
return null;
106+
}
107+
serializer = new DefaultSerializer(logger);
108+
final JsonObject[] sourceArray = serializer.deserializeObject(json.toString(), JsonObject[].class);
109+
final ArrayList<T1> list = new ArrayList<T1>(sourceArray.length);
110+
/** eg: com.microsoft.graph.requests.extensions.AttachmentCollectionPage */
111+
final String collectionPageClassCanonicalName = typeOfT.getTypeName();
112+
/** eg: com.microsoft.graph.models.extensions.Attachment */
113+
final String baseEntityClassCanonicalName = collectionPageClassCanonicalName
114+
.substring(0, collectionPageClassCanonicalName.length() - pageLength - collectionLength)
115+
.replace("requests", "models");
116+
try {
117+
final Class<?> baseEntityClass = Class.forName(baseEntityClassCanonicalName);
118+
for (JsonObject sourceObject : sourceArray) {
119+
Class<?> entityClass = serializer.getDerivedClass(sourceObject, baseEntityClass);
120+
if(entityClass == null)
121+
entityClass = baseEntityClass;
122+
final T1 targetObject = (T1)serializer.deserializeObject(sourceObject.toString(), entityClass);
123+
((IJsonBackedObject)targetObject).setRawObject(serializer, sourceObject);
124+
list.add(targetObject);
125+
}
126+
/** eg: com.microsoft.graph.requests.extensions.AttachmentCollectionResponse */
127+
final String responseClassCanonicalName = collectionPageClassCanonicalName
128+
.substring(0, collectionPageClassCanonicalName.length() - pageLength) + "Response";
129+
final Class<?> responseClass = Class.forName(responseClassCanonicalName);
130+
final Object response = responseClass.getConstructor().newInstance();
131+
responseClass.getField("value").set(response, list);
132+
final Class<?> collectionPageClass = Class.forName(collectionPageClassCanonicalName);
133+
/** eg: com.microsoft.graph.requests.extensions.IAttachmentCollectionRequestBuilder */
134+
final String responseBuilderInterfaceCanonicalName = responseClassCanonicalName
135+
.substring(0, responseClassCanonicalName.length() - responseLength)
136+
.replace(extensionsPath, extensionsPath + "I") + "RequestBuilder";
137+
final Class<?> responseBuilderInterfaceClass = Class.forName(responseBuilderInterfaceCanonicalName);
138+
return (BaseCollectionPage<T1, T2>)collectionPageClass.getConstructor(responseClass, responseBuilderInterfaceClass).newInstance(response, null);
139+
} catch(ClassNotFoundException ex) {
140+
logger.logError("Could not find class during deserialization", ex);
141+
} catch(NoSuchMethodException | InstantiationException | InvocationTargetException ex) {
142+
logger.logError("Could not instanciate type during deserialization", ex);
143+
} catch(NoSuchFieldException | IllegalAccessException ex) {
144+
logger.logError("Unable to set field value during deserialization", ex);
145+
}
146+
return null;
147+
}
148+
}

src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,18 @@ private void addAdditionalDataToJson(AdditionalDataManager additionalDataManager
327327
* @param parentClass the parent class the derived class should inherit from
328328
* @return the derived class if found, or null if not applicable
329329
*/
330-
private Class<?> getDerivedClass(JsonObject jsonObject, Class<?> parentClass) {
331-
//Identify the odata.type information if provided
332-
if (jsonObject.get("@odata.type") != null) {
333-
String odataType = jsonObject.get("@odata.type").getAsString();
334-
String derivedType = odataType.substring(odataType.lastIndexOf('.') + 1); //Remove microsoft.graph prefix
335-
derivedType = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, derivedType);
336-
derivedType = "com.microsoft.graph.models.extensions." + derivedType; //Add full package path
337-
330+
private final static String ODATA_TYPE_KEY = "@odata.type";
331+
public Class<?> getDerivedClass(JsonObject jsonObject, Class<?> parentClass) {
332+
//Identify the odata.type information if provided
333+
if (jsonObject.get(ODATA_TYPE_KEY) != null) {
334+
/** #microsoft.graph.user or #microsoft.graph.callrecords.callrecord */
335+
final String odataType = jsonObject.get(ODATA_TYPE_KEY).getAsString();
336+
final Integer lastDotIndex = odataType.lastIndexOf(".");
337+
final String derivedType = (odataType.substring(0, lastDotIndex) +
338+
".models.extensions." +
339+
CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
340+
odataType.substring(lastDotIndex + 1)))
341+
.replace("#", "com.");
338342
try {
339343
Class<?> derivedClass = Class.forName(derivedType);
340344
//Check that the derived class inherits from the given parent class

src/main/java/com/microsoft/graph/serializer/GsonFactory.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
import com.google.gson.JsonPrimitive;
3232
import com.google.gson.JsonSerializationContext;
3333
import com.google.gson.JsonSerializer;
34+
import com.microsoft.graph.http.BaseCollectionPage;
3435
import com.microsoft.graph.logger.ILogger;
3536
import com.microsoft.graph.models.extensions.DateOnly;
36-
import com.microsoft.graph.requests.extensions.AttachmentCollectionPage;
3737

3838
import com.microsoft.graph.models.extensions.TimeOfDay;
3939
import java.lang.reflect.Type;
@@ -212,21 +212,21 @@ public Duration deserialize(final JsonElement json,
212212
}
213213
};
214214

215-
final JsonSerializer<AttachmentCollectionPage> attachmentCollectionPageSerializer = new JsonSerializer<AttachmentCollectionPage>() {
215+
final JsonSerializer<BaseCollectionPage<?,?>> collectionPageSerializer = new JsonSerializer<BaseCollectionPage<?,?>>() {
216216
@Override
217-
public JsonElement serialize(final AttachmentCollectionPage src,
217+
public JsonElement serialize(final BaseCollectionPage<?,?> src,
218218
final Type typeOfSrc,
219219
final JsonSerializationContext context) {
220-
return AttachmentCollectionPageSerializer.serialize(src, logger);
220+
return CollectionPageSerializer.serialize(src, logger);
221221
}
222222
};
223223

224-
final JsonDeserializer<AttachmentCollectionPage> attachmentCollectionPageDeserializer = new JsonDeserializer<AttachmentCollectionPage>() {
224+
final JsonDeserializer<BaseCollectionPage<?,?>> collectionPageDeserializer = new JsonDeserializer<BaseCollectionPage<?,?>>() {
225225
@Override
226-
public AttachmentCollectionPage deserialize(final JsonElement json,
226+
public BaseCollectionPage<?,?> deserialize(final JsonElement json,
227227
final Type typeOfT,
228228
final JsonDeserializationContext context) throws JsonParseException {
229-
return AttachmentCollectionPageSerializer.deserialize(json, logger);
229+
return CollectionPageSerializer.deserialize(json, typeOfT, logger);
230230
}
231231
};
232232

@@ -257,8 +257,8 @@ public TimeOfDay deserialize(final JsonElement json,
257257
.registerTypeAdapter(EnumSet.class, enumSetJsonDeserializer)
258258
.registerTypeAdapter(Duration.class, durationJsonSerializer)
259259
.registerTypeAdapter(Duration.class, durationJsonDeserializer)
260-
.registerTypeAdapter(AttachmentCollectionPage.class, attachmentCollectionPageSerializer)
261-
.registerTypeAdapter(AttachmentCollectionPage.class, attachmentCollectionPageDeserializer)
260+
.registerTypeHierarchyAdapter(BaseCollectionPage.class, collectionPageSerializer)
261+
.registerTypeHierarchyAdapter(BaseCollectionPage.class, collectionPageDeserializer)
262262
.registerTypeAdapter(TimeOfDay.class, timeOfDayJsonDeserializer)
263263
.registerTypeAdapterFactory(new FallbackTypeAdapterFactory(logger))
264264
.create();

0 commit comments

Comments
 (0)