From afa91d91484fe7fb00f32e63de3a0667f917da78 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 18 Sep 2020 14:14:09 -0400 Subject: [PATCH 1/4] - fixes a bug where collections would not be deserialized properly --- .../AttachmentCollectionPageSerializer.java | 95 ------------ .../serializer/CollectionPageSerializer.java | 138 ++++++++++++++++++ .../graph/serializer/GsonFactory.java | 18 +-- ...ava => CollectionPageSerializerTests.java} | 20 ++- 4 files changed, 164 insertions(+), 107 deletions(-) delete mode 100644 src/main/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializer.java create mode 100644 src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java rename src/test/java/com/microsoft/graph/serializer/{AttachmentCollectionPageSerializerTests.java => CollectionPageSerializerTests.java} (66%) diff --git a/src/main/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializer.java b/src/main/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializer.java deleted file mode 100644 index 19f4e12010f..00000000000 --- a/src/main/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializer.java +++ /dev/null @@ -1,95 +0,0 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) 2017 Microsoft Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// ------------------------------------------------------------------------------ - -package com.microsoft.graph.serializer; -import java.util.Arrays; -import java.util.List; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.microsoft.graph.logger.ILogger; -import com.microsoft.graph.models.extensions.Attachment; -import com.microsoft.graph.requests.extensions.AttachmentCollectionPage; -import com.microsoft.graph.requests.extensions.AttachmentCollectionResponse; - -public class AttachmentCollectionPageSerializer { - - private static DefaultSerializer serializer; - - /** - * Not available for instantiation - */ - private AttachmentCollectionPageSerializer() { - } - - /** - * Serializes an AttachmentCollectionPage - * - * @param src the AttachmentCollectionPage variable for serialization - * @param logger the logger - * @return JsonElement of AttachmentCollectionPage - */ - public static JsonElement serialize(final AttachmentCollectionPage src, final ILogger logger) { - if(src == null) { - return null; - } - JsonArray jsonArray = new JsonArray(); - List attachments = src.getCurrentPage(); - serializer = new DefaultSerializer(logger); - String json; - JsonObject jsonObject; - for(Attachment attachment : attachments) { - jsonObject = new JsonObject(); - json = serializer.serializeObject(attachment); - jsonObject = (JsonObject)JsonParser.parseString(json); - jsonArray.add(jsonObject); - } - return jsonArray; - } - - /** - * Deserializes the JsonElement - * - * @param json the source AttachmentCollectionPage's Json - * @param logger the logger - * @throws JsonParseException the parse exception - * @return the deserialized AttachmentCollectionPage - */ - public static AttachmentCollectionPage deserialize(final JsonElement json, final ILogger logger) throws JsonParseException { - if (json == null) { - return null; - } - final AttachmentCollectionResponse response = new AttachmentCollectionResponse(); - serializer = new DefaultSerializer(logger); - final JsonObject[] sourceArray = serializer.deserializeObject(json.toString(), JsonObject[].class); - final Attachment[] array = new Attachment[sourceArray.length]; - for (int i = 0; i < sourceArray.length; i++) { - array[i] = serializer.deserializeObject(sourceArray[i].toString(), Attachment.class); - array[i].setRawObject(serializer, sourceArray[i]); - } - response.value = Arrays.asList(array); - return new AttachmentCollectionPage(response, null); - } -} \ No newline at end of file diff --git a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java new file mode 100644 index 00000000000..70c4372fb24 --- /dev/null +++ b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java @@ -0,0 +1,138 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2017 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.serializer; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.common.reflect.TypeToken; +import com.microsoft.graph.http.BaseCollectionPage; +import com.microsoft.graph.http.IRequestBuilder; +import com.microsoft.graph.logger.ILogger; + +// those imports are useless but build will fail if code-gen conventions change, keep it +import com.microsoft.graph.models.extensions.Attachment; +import com.microsoft.graph.requests.extensions.AttachmentCollectionPage; +import com.microsoft.graph.requests.extensions.AttachmentCollectionResponse; +import com.microsoft.graph.requests.extensions.IAttachmentCollectionRequestBuilder; + +public class CollectionPageSerializer { + + private static DefaultSerializer serializer; + /** length of the word "page" */ + private final static Integer pageLength = 4; + /** length of the word "collection" */ + private final static Integer collectionLength = 10; + /** length of the work "response" */ + private final static Integer responseLength = 8; + /** the extensions segment in the package name of target classes */ + private final static String extensionsPath = "extensions."; + + /** + * Not available for instantiation + */ + private CollectionPageSerializer() { + } + + /** + * Serializes an CollectionPage + * + * @param src the CollectionPage variable for serialization + * @param logger the logger + * @return JsonElement of CollectionPage + */ + public static JsonElement serialize(final BaseCollectionPage src, final ILogger logger) { + if(src == null) { + return null; + } + JsonArray jsonArray = new JsonArray(); + List items = src.getCurrentPage(); + serializer = new DefaultSerializer(logger); + for(T1 item : items) { + final String json = serializer.serializeObject(item); + final JsonElement element = JsonParser.parseString(json); + if(element != null && element.isJsonObject()) { + final JsonObject jsonObject = element.getAsJsonObject(); + jsonArray.add(jsonObject); + } + } + return jsonArray; + } + + /** + * Deserializes the JsonElement + * + * @param json the source CollectionPage's Json + * @param typeOfT The type of the CollectionPage to deserialize to + * @param logger the logger + * @throws JsonParseException the parse exception + * @return the deserialized CollectionPage + */ + @SuppressWarnings("unchecked") + public static BaseCollectionPage deserialize(final JsonElement json, Type typeOfT, final ILogger logger) throws JsonParseException { + if (json == null) { + return null; + } + serializer = new DefaultSerializer(logger); + final JsonObject[] sourceArray = serializer.deserializeObject(json.toString(), JsonObject[].class); + final ArrayList list = new ArrayList(sourceArray.length); + final String collectionPageClassCanonicalName = typeOfT.getTypeName(); + final String entityClassCanonicalName = collectionPageClassCanonicalName + .substring(0, collectionPageClassCanonicalName.length() - pageLength - collectionLength) + .replace("requests", "models"); + try { + final Class entityClass = Class.forName(entityClassCanonicalName); + for (JsonObject sourceObject : sourceArray) { + final T1 targetObject = (T1)serializer.deserializeObject(sourceObject.toString(), entityClass); + ((IJsonBackedObject)targetObject).setRawObject(serializer, sourceObject); + list.add(targetObject); + } + final String responseClassCanonicalName = collectionPageClassCanonicalName + .substring(0, collectionPageClassCanonicalName.length() - pageLength) + "Response"; + final Class responseClass = Class.forName(responseClassCanonicalName); + final Object response = responseClass.getConstructor().newInstance(); + responseClass.getField("value").set(response, list); + final Class collectionPageClass = Class.forName(collectionPageClassCanonicalName); + final String responseBuilderInterfaceCanonicalName = responseClassCanonicalName + .substring(0, responseClassCanonicalName.length() - responseLength) + .replace(extensionsPath, extensionsPath + "I") + "RequestBuilder"; + final Class responseBuilderInterfaceClass = Class.forName(responseBuilderInterfaceCanonicalName); + return (BaseCollectionPage)collectionPageClass.getConstructor(responseClass, responseBuilderInterfaceClass).newInstance(response, null); + } catch(ClassNotFoundException ex) { + logger.logError("Could not find class during deserialization", ex); + } catch(NoSuchMethodException | InstantiationException | InvocationTargetException ex) { + logger.logError("Could not instanciate type during deserialization", ex); + } catch(NoSuchFieldException | IllegalAccessException ex) { + logger.logError("Unable to set field value during deserialization", ex); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/microsoft/graph/serializer/GsonFactory.java b/src/main/java/com/microsoft/graph/serializer/GsonFactory.java index 32bf0668700..f593d6be007 100644 --- a/src/main/java/com/microsoft/graph/serializer/GsonFactory.java +++ b/src/main/java/com/microsoft/graph/serializer/GsonFactory.java @@ -31,9 +31,9 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import com.microsoft.graph.http.BaseCollectionPage; import com.microsoft.graph.logger.ILogger; import com.microsoft.graph.models.extensions.DateOnly; -import com.microsoft.graph.requests.extensions.AttachmentCollectionPage; import com.microsoft.graph.models.extensions.TimeOfDay; import java.lang.reflect.Type; @@ -212,21 +212,21 @@ public Duration deserialize(final JsonElement json, } }; - final JsonSerializer attachmentCollectionPageSerializer = new JsonSerializer() { + final JsonSerializer> collectionPageSerializer = new JsonSerializer>() { @Override - public JsonElement serialize(final AttachmentCollectionPage src, + public JsonElement serialize(final BaseCollectionPage src, final Type typeOfSrc, final JsonSerializationContext context) { - return AttachmentCollectionPageSerializer.serialize(src, logger); + return CollectionPageSerializer.serialize(src, logger); } }; - final JsonDeserializer attachmentCollectionPageDeserializer = new JsonDeserializer() { + final JsonDeserializer> collectionPageDeserializer = new JsonDeserializer>() { @Override - public AttachmentCollectionPage deserialize(final JsonElement json, + public BaseCollectionPage deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { - return AttachmentCollectionPageSerializer.deserialize(json, logger); + return CollectionPageSerializer.deserialize(json, typeOfT, logger); } }; @@ -257,8 +257,8 @@ public TimeOfDay deserialize(final JsonElement json, .registerTypeAdapter(EnumSet.class, enumSetJsonDeserializer) .registerTypeAdapter(Duration.class, durationJsonSerializer) .registerTypeAdapter(Duration.class, durationJsonDeserializer) - .registerTypeAdapter(AttachmentCollectionPage.class, attachmentCollectionPageSerializer) - .registerTypeAdapter(AttachmentCollectionPage.class, attachmentCollectionPageDeserializer) + .registerTypeHierarchyAdapter(BaseCollectionPage.class, collectionPageSerializer) + .registerTypeHierarchyAdapter(BaseCollectionPage.class, collectionPageDeserializer) .registerTypeAdapter(TimeOfDay.class, timeOfDayJsonDeserializer) .registerTypeAdapterFactory(new FallbackTypeAdapterFactory(logger)) .create(); diff --git a/src/test/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializerTests.java b/src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java similarity index 66% rename from src/test/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializerTests.java rename to src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java index 7f0fb743e1d..42c0caad539 100644 --- a/src/test/java/com/microsoft/graph/serializer/AttachmentCollectionPageSerializerTests.java +++ b/src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java @@ -1,6 +1,8 @@ package com.microsoft.graph.serializer; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -11,11 +13,13 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import com.microsoft.graph.http.BaseCollectionPage; import com.microsoft.graph.logger.MockLogger; import com.microsoft.graph.models.extensions.Attachment; import com.microsoft.graph.models.extensions.Attendee; import com.microsoft.graph.models.extensions.Contact; import com.microsoft.graph.models.extensions.DateTimeTimeZone; +import com.microsoft.graph.models.extensions.DriveItem; import com.microsoft.graph.models.extensions.EmailAddress; import com.microsoft.graph.models.extensions.Event; import com.microsoft.graph.models.extensions.FileAttachment; @@ -23,7 +27,7 @@ import com.microsoft.graph.requests.extensions.AttachmentCollectionPage; import com.microsoft.graph.requests.extensions.AttachmentCollectionResponse; -public class AttachmentCollectionPageSerializerTests { +public class CollectionPageSerializerTests { private MockLogger logger; @Before public void setUp() { @@ -36,7 +40,7 @@ public void testAttachmentCollectionPageSerialization() throws Exception { AttachmentCollectionResponse response = new AttachmentCollectionResponse(); response.value = Arrays.asList(getFileAttachment(),getItemAttachmentWithEvent(),getItemAttachmentWithContact()); AttachmentCollectionPage attachmentCollectionPage = new AttachmentCollectionPage(response, null); - JsonElement serializedJson = AttachmentCollectionPageSerializer.serialize(attachmentCollectionPage, logger); + JsonElement serializedJson = CollectionPageSerializer.serialize(attachmentCollectionPage, logger); logger.logDebug(serializedJson.toString()); assertEquals(expectedString,serializedJson.toString()); } @@ -45,7 +49,7 @@ public void testAttachmentCollectionPageSerialization() throws Exception { public void testAttachmentCollectionPageDeserialization() throws Exception { String jsonString = "[{\"contentBytes\":\"ZGF0YQ==\",\"name\":\"document.pdf\",\"@odata.type\":\"#microsoft.graph.fileAttachment\",\"id\":\"54321\"},{\"@odata.type\":\"#microsoft.graph.itemAttachment\",\"name\":\"Holiday event\",\"id\":null,\"isInline\":null,\"size\":null,\"item\":{\"subject\":\"Test Event Subject\",\"start\":{\"dateTime\":\"2018-10-16T06:15:26.544Z\",\"timeZone\":\"UTC\"},\"end\":{\"dateTime\":\"2018-11-18T07:30:26.544Z\",\"timeZone\":\"UTC\"},\"@odata.type\":\"microsoft.graph.event\",\"id\":\"1234\"}},{\"@odata.type\":\"#microsoft.graph.itemAttachment\",\"name\":\"Attachment name\",\"id\":null,\"isInline\":null,\"size\":null,\"item\":{\"displayName\":\"displayname\",\"mobilePhone\":\"123456890\",\"@odata.type\":\"microsoft.graph.contact\"}}]"; JsonElement jsonElement = JsonParser.parseString(jsonString); - AttachmentCollectionPage attachmentCollectionPage = AttachmentCollectionPageSerializer.deserialize(jsonElement, logger); + BaseCollectionPage attachmentCollectionPage = CollectionPageSerializer.deserialize(jsonElement, AttachmentCollectionPage.class, logger); for(Attachment attachment: attachmentCollectionPage.getCurrentPage()) { if(attachment instanceof FileAttachment) { FileAttachment fileAttachment = (FileAttachment)attachment; @@ -69,6 +73,16 @@ else if(itemAttachment.item instanceof Contact) { } } + @Test + public void testEntityWithCollectionOnDefaultDeserializer() throws Exception { + final String jsonString = "{\"createdBy\":{\"application\":{\"displayName\":\"UmtPlus\",\"id\":\"4458250c\"},\"user\":{\"id\":\"c1fba35378bf924c\"}},\"createdDateTime\":\"2020-09-16T14:53:53.61Z\",\"cTag\":\"aYzpDMUZCQTM1Mzc4QkY5MjRDITIzNDkyNS4yNTc\",\"eTag\":\"aQzFGQkEzNTM3OEJGOTI0QyEyMzQ5MjUuMTE\",\"id\":\"C1FBA35378BF924C!234925\",\"lastModifiedBy\":{\"application\":{\"displayName\":\"UmtPlus\",\"id\":\"4458250c\"},\"user\":{\"id\":\"c1fba35378bf924c\"}},\"lastModifiedDateTime\":\"2020-09-16T17:42:17.847Z\",\"name\":\"Change Flat Tire.sco\",\"parentReference\":{\"driveId\":\"c1fba35378bf924c\",\"driveType\":\"personal\",\"id\":\"C1FBA35378BF924C!234867\",\"name\":\"UmtPlus\",\"path\":\"/drive/root:/UmtPlus\"},\"size\":59228,\"webUrl\":\"https://1drv.ms/u/s!AEySv3hTo_vBjqst\",\"items\":[],\"file\":{\"hashes\":{\"quickXorHash\":\"RjqF6zG7yzMKxLlRmXkKr0tK7oQ=\",\"sha1Hash\":\"A7A1DB7C7355A372E6097C5BD7DF6CF702AFA897\",\"sha256Hash\":\"97EF73D523368EE939D084F87DE22E28BD9236CC55D6A67EE69183FFC456CA08\"},\"mimeType\":\"application/octet-stream\"},\"fileSystemInfo\":{\"createdDateTime\":\"2020-09-16T14:53:53.61Z\",\"lastModifiedDateTime\":\"2020-09-16T17:42:17.846Z\"},\"reactions\":{\"commentCount\":0},\"tags\":[],\"lenses\":[],\"thumbnails\":[{\"id\":\"0\",\"large\":{\"height\":800,\"url\":\"https://oxo45g.bl.files.1drv.com/y4pi3j1XhJr0-LmucbMAY7erAc5yeeX8yXaxUqk7p5O1mYVUMnRmzIeFC8LgpZLXCNFkFfVzt_PlChpIBL2VwTp9bdXVToVWsHRKC5MmEiO4Zv3eR9_JCc2ih4jstMbx6AusvkIpCW7FEpWWSeyFQEJR0jbaNNZSs_n6Ryrio2xYl9LhINf19-xYBxVCR4kV188?width=800&height=800&cropmode=none\",\"width\":800},\"medium\":{\"height\":176,\"url\":\"https://oxo45g.bl.files.1drv.com/y4pi3j1XhJr0-LmucbMAY7erAc5yeeX8yXaxUqk7p5O1mYVUMnRmzIeFC8LgpZLXCNFkFfVzt_PlChpIBL2VwTp9bdXVToVWsHRKC5MmEiO4Zv3eR9_JCc2ih4jstMbx6AusvkIpCW7FEpWWSeyFQEJR0jbaNNZSs_n6Ryrio2xYl9LhINf19-xYBxVCR4kV188?width=176&height=176&cropmode=none\",\"width\":176},\"small\":{\"height\":96,\"url\":\"https://oxo45g.bl.files.1drv.com/y4pi3j1XhJr0-LmucbMAY7erAc5yeeX8yXaxUqk7p5O1mYVUMnRmzIeFC8LgpZLXCNFkFfVzt_PlChpIBL2VwTp9bdXVToVWsHRKC5MmEiO4Zv3eR9_JCc2ih4jstMbx6AusvkIpCW7FEpWWSeyFQEJR0jbaNNZSs_n6Ryrio2xYl9LhINf19-xYBxVCR4kV188?width=96&height=96&cropmode=none\",\"width\":96}}]}"; + final DefaultSerializer defaultSerializer = new DefaultSerializer(logger); + final DriveItem driveItem = defaultSerializer.deserializeObject(jsonString, DriveItem.class); + assertNotNull(driveItem); + assertNotNull(driveItem.thumbnails); + assertTrue(driveItem.thumbnails.getCurrentPage().size() > 0); + } + private FileAttachment getFileAttachment() throws Exception{ FileAttachment fileAttachment = new FileAttachment(); fileAttachment.name = "document.pdf"; From 9a904f4cbe0f73082093df0112c404f0d46d67cc Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 21 Sep 2020 09:03:47 -0400 Subject: [PATCH 2/4] - adds comments to ease reading --- .../graph/serializer/CollectionPageSerializer.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java index 70c4372fb24..cf065da7286 100644 --- a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java +++ b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java @@ -38,7 +38,10 @@ import com.microsoft.graph.http.IRequestBuilder; import com.microsoft.graph.logger.ILogger; -// those imports are useless but build will fail if code-gen conventions change, keep it +// keep these imports: even though they are not used, it's a good way to call +// for maintainer's attention en the event code generation naming conventions +// change. If the name change, these import will build at build time rather than +// reflection building at run time. import com.microsoft.graph.models.extensions.Attachment; import com.microsoft.graph.requests.extensions.AttachmentCollectionPage; import com.microsoft.graph.requests.extensions.AttachmentCollectionResponse; @@ -104,7 +107,9 @@ public static BaseCollectionPage deseri serializer = new DefaultSerializer(logger); final JsonObject[] sourceArray = serializer.deserializeObject(json.toString(), JsonObject[].class); final ArrayList list = new ArrayList(sourceArray.length); + /** eg: com.microsoft.graph.requests.extensions.AttachmentCollectionPage */ final String collectionPageClassCanonicalName = typeOfT.getTypeName(); + /** eg: com.microsoft.graph.models.extensions.Attachment */ final String entityClassCanonicalName = collectionPageClassCanonicalName .substring(0, collectionPageClassCanonicalName.length() - pageLength - collectionLength) .replace("requests", "models"); @@ -115,12 +120,14 @@ public static BaseCollectionPage deseri ((IJsonBackedObject)targetObject).setRawObject(serializer, sourceObject); list.add(targetObject); } + /** eg: com.microsoft.graph.requests.extensions.AttachmentCollectionResponse */ final String responseClassCanonicalName = collectionPageClassCanonicalName .substring(0, collectionPageClassCanonicalName.length() - pageLength) + "Response"; final Class responseClass = Class.forName(responseClassCanonicalName); final Object response = responseClass.getConstructor().newInstance(); responseClass.getField("value").set(response, list); final Class collectionPageClass = Class.forName(collectionPageClassCanonicalName); + /** eg: com.microsoft.graph.requests.extensions.IAttachmentCollectionRequestBuilder */ final String responseBuilderInterfaceCanonicalName = responseClassCanonicalName .substring(0, responseClassCanonicalName.length() - responseLength) .replace(extensionsPath, extensionsPath + "I") + "RequestBuilder"; From b343dcdfe2a179e9907a03404c604364098faef6 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 21 Sep 2020 10:02:59 -0400 Subject: [PATCH 3/4] - adds support for derived types in collection deserialization --- .../serializer/CollectionPageSerializer.java | 7 +++++-- .../graph/serializer/DefaultSerializer.java | 20 +++++++++++-------- .../CollectionPageSerializerTests.java | 18 +++++++++++++++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java index cf065da7286..1d8f21c6145 100644 --- a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java +++ b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java @@ -110,12 +110,15 @@ public static BaseCollectionPage deseri /** eg: com.microsoft.graph.requests.extensions.AttachmentCollectionPage */ final String collectionPageClassCanonicalName = typeOfT.getTypeName(); /** eg: com.microsoft.graph.models.extensions.Attachment */ - final String entityClassCanonicalName = collectionPageClassCanonicalName + final String baseEntityClassCanonicalName = collectionPageClassCanonicalName .substring(0, collectionPageClassCanonicalName.length() - pageLength - collectionLength) .replace("requests", "models"); try { - final Class entityClass = Class.forName(entityClassCanonicalName); + final Class baseEntityClass = Class.forName(baseEntityClassCanonicalName); for (JsonObject sourceObject : sourceArray) { + Class entityClass = serializer.getDerivedClass(sourceObject, baseEntityClass); + if(entityClass == null) + entityClass = baseEntityClass; final T1 targetObject = (T1)serializer.deserializeObject(sourceObject.toString(), entityClass); ((IJsonBackedObject)targetObject).setRawObject(serializer, sourceObject); list.add(targetObject); diff --git a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java index bbac7846129..c757195e4eb 100644 --- a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java +++ b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java @@ -313,14 +313,18 @@ private void addAdditionalDataToJson(AdditionalDataManager additionalDataManager * @param parentClass the parent class the derived class should inherit from * @return the derived class if found, or null if not applicable */ - private Class getDerivedClass(JsonObject jsonObject, Class parentClass) { - //Identify the odata.type information if provided - if (jsonObject.get("@odata.type") != null) { - String odataType = jsonObject.get("@odata.type").getAsString(); - String derivedType = odataType.substring(odataType.lastIndexOf('.') + 1); //Remove microsoft.graph prefix - derivedType = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, derivedType); - derivedType = "com.microsoft.graph.models.extensions." + derivedType; //Add full package path - + private final static String ODATA_TYPE_KEY = "@odata.type"; + public Class getDerivedClass(JsonObject jsonObject, Class parentClass) { + //Identify the odata.type information if provided + if (jsonObject.get(ODATA_TYPE_KEY) != null) { + /** #microsoft.graph.user or #microsoft.graph.callrecords.callrecord */ + final String odataType = jsonObject.get(ODATA_TYPE_KEY).getAsString(); + final Integer lastDotIndex = odataType.lastIndexOf("."); + final String derivedType = (odataType.substring(0, lastDotIndex) + + ".models.extensions." + + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + odataType.substring(lastDotIndex + 1))) + .replace("#", "com."); try { Class derivedClass = Class.forName(derivedType); //Check that the derived class inherits from the given parent class diff --git a/src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java b/src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java index 42c0caad539..53753929a55 100644 --- a/src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java +++ b/src/test/java/com/microsoft/graph/serializer/CollectionPageSerializerTests.java @@ -7,6 +7,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.junit.Before; import org.junit.Test; @@ -19,11 +20,15 @@ import com.microsoft.graph.models.extensions.Attendee; import com.microsoft.graph.models.extensions.Contact; import com.microsoft.graph.models.extensions.DateTimeTimeZone; +import com.microsoft.graph.models.extensions.DirectoryObject; import com.microsoft.graph.models.extensions.DriveItem; import com.microsoft.graph.models.extensions.EmailAddress; import com.microsoft.graph.models.extensions.Event; import com.microsoft.graph.models.extensions.FileAttachment; +import com.microsoft.graph.models.extensions.Group; import com.microsoft.graph.models.extensions.ItemAttachment; +import com.microsoft.graph.models.extensions.ServicePrincipal; +import com.microsoft.graph.models.extensions.User; import com.microsoft.graph.requests.extensions.AttachmentCollectionPage; import com.microsoft.graph.requests.extensions.AttachmentCollectionResponse; @@ -83,6 +88,19 @@ public void testEntityWithCollectionOnDefaultDeserializer() throws Exception { assertTrue(driveItem.thumbnails.getCurrentPage().size() > 0); } + @Test + public void testEntityWithCollectionOfMultipleTypes() throws Exception { + final String jsonString = "{\"@odata.context\": \"https://graph.microsoft.com/v1.0/$metadata#groups/$entity\",\"id\": \"01b4b70e-2ea6-432f-a3d7-eefd826c2a8e\",\"deletedDateTime\": null,\"classification\": null,\"createdDateTime\": \"2019-11-23T15:48:33Z\",\"creationOptions\": [],\"description\": \"wovinewovinewvoinwev\",\"displayName\": \"Toronto Basketball Raptors\",\"expirationDateTime\": null,\"groupTypes\": [\"Unified\"],\"isAssignableToRole\": null,\"mail\": \"TBR@contoso.onmicrosoft.com\",\"mailEnabled\": true,\"mailNickname\": \"TBR\",\"membershipRule\": null,\"membershipRuleProcessingState\": null,\"onPremisesDomainName\": null,\"onPremisesLastSyncDateTime\": null,\"onPremisesNetBiosName\": null,\"onPremisesSamAccountName\": null,\"onPremisesSecurityIdentifier\": null,\"onPremisesSyncEnabled\": null,\"preferredDataLocation\": null,\"preferredLanguage\": null,\"proxyAddresses\": [\"SPO:SPO_c0b1d860-d3d3-444c-98ee-c34365caa414@SPO_bd4c6c31-c49c-4ab6-a0aa-742e07c20232\",\"SMTP:TBR@contoso.onmicrosoft.com\"],\"renewedDateTime\": \"2019-11-23T15:48:33Z\",\"resourceBehaviorOptions\": [],\"resourceProvisioningOptions\": [\"Team\"],\"securityEnabled\": false,\"securityIdentifier\": \"S-1-12-1-28620558-1127165606-4260288419-2385144962\",\"theme\": null,\"visibility\": \"Private\",\"onPremisesProvisioningErrors\": [],\"members\": [{\"@odata.type\": \"#microsoft.graph.user\",\"id\": \"c2e8df37-c6a7-4d88-89b1-feb4f1fda7c5\",\"businessPhones\": [\"4388888888\"],\"displayName\": \"Vincent Biret\",\"givenName\": \"Vincent\",\"jobTitle\": null,\"mail\": \"vincent@contoso.onmicrosoft.com\",\"mobilePhone\": \"+1 4388888888\",\"officeLocation\": \"Quebec\",\"preferredLanguage\": \"en-US\",\"surname\": \"Biret\",\"userPrincipalName\": \"vincent@contoso.onmicrosoft.com\"},{\"@odata.type\": \"#microsoft.graph.servicePrincipal\",\"id\": \"004ea702-a572-4f1b-8bb0-74598985e0c0\",\"deletedDateTime\": null,\"accountEnabled\": true,\"alternativeNames\": [],\"appDisplayName\": \"OCPS Checkin Service\",\"appDescription\": null,\"appId\": \"23c898c1-f7e8-41da-9501-f16571f8d097\",\"applicationTemplateId\": null,\"appOwnerOrganizationId\": \"f8cdef31-a31e-4b4a-93e4-5f571e91255a\",\"appRoleAssignmentRequired\": false,\"createdDateTime\": \"2018-11-13T02:28:32Z\",\"description\": null,\"displayName\": \"OCPS Checkin Service\",\"homepage\": null,\"loginUrl\": null,\"logoutUrl\": null,\"notes\": null,\"notificationEmailAddresses\": [],\"preferredSingleSignOnMode\": null,\"preferredTokenSigningKeyThumbprint\": null,\"replyUrls\": [\"https://ocps.manage.microsoft.com\"],\"resourceSpecificApplicationPermissions\": [],\"samlSingleSignOnSettings\": null,\"servicePrincipalNames\": [\"23c898c1-f7e8-41da-9501-f16571f8d097\",\"https://ocps.manage.microsoft.com\"],\"servicePrincipalType\": \"Application\",\"tags\": [],\"tokenEncryptionKeyId\": null,\"verifiedPublisher\": {\"displayName\": null,\"verifiedPublisherId\": null,\"addedDateTime\": null},\"addIns\": [],\"appRoles\": [],\"info\": {\"logoUrl\": null,\"marketingUrl\": null,\"privacyStatementUrl\": null,\"supportUrl\": null,\"termsOfServiceUrl\": null},\"keyCredentials\": [],\"oauth2PermissionScopes\": [],\"passwordCredentials\": []}]}"; + final DefaultSerializer defaultSerializer = new DefaultSerializer(logger); + final Group group = defaultSerializer.deserializeObject(jsonString, Group.class); + assertNotNull(group); + assertNotNull(group.members); + final List page = group.members.getCurrentPage(); + assertTrue(page.size() == 2); + assertTrue(page.get(0) instanceof User); + assertTrue(page.get(1) instanceof ServicePrincipal); + } + private FileAttachment getFileAttachment() throws Exception{ FileAttachment fileAttachment = new FileAttachment(); fileAttachment.name = "document.pdf"; From 05928f8fd1274db066f35f7a086eae7c66da08cb Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 21 Sep 2020 10:06:16 -0400 Subject: [PATCH 4/4] - fixes tabing --- .../microsoft/graph/serializer/DefaultSerializer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java index c757195e4eb..6a04a356339 100644 --- a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java +++ b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java @@ -318,12 +318,12 @@ public Class getDerivedClass(JsonObject jsonObject, Class parentClass) { //Identify the odata.type information if provided if (jsonObject.get(ODATA_TYPE_KEY) != null) { /** #microsoft.graph.user or #microsoft.graph.callrecords.callrecord */ - final String odataType = jsonObject.get(ODATA_TYPE_KEY).getAsString(); + final String odataType = jsonObject.get(ODATA_TYPE_KEY).getAsString(); final Integer lastDotIndex = odataType.lastIndexOf("."); - final String derivedType = (odataType.substring(0, lastDotIndex) + - ".models.extensions." + - CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, - odataType.substring(lastDotIndex + 1))) + final String derivedType = (odataType.substring(0, lastDotIndex) + + ".models.extensions." + + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + odataType.substring(lastDotIndex + 1))) .replace("#", "com."); try { Class derivedClass = Class.forName(derivedType);