Skip to content

Commit 8e2dc4b

Browse files
authored
Support BinaryData serialization (#45423)
* Support serializing List<BinaryData> in JsonSerializer * update tests * update serializer * fix checkstyle * pr comments * handle logging * use reflection utils * update unit test
1 parent 8e6c7bc commit 8e2dc4b

File tree

6 files changed

+117
-52
lines changed

6 files changed

+117
-52
lines changed

sdk/clientcore/core/spotbugs-exclude.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
<Class name="io.clientcore.core.serialization.xml.XmlReader" />
130130
<Class name="io.clientcore.core.shared.HttpClientTests" />
131131
<Class name="io.clientcore.core.shared.HttpClientTestsServer" />
132-
<Class name="io.clientcore.core.utils.serializers.JsonSerializerTests" />
132+
<Class name="io.clientcore.core.serialization.json.JsonSerializerTests" />
133133
</Or>
134134
</Match>
135135
<Match>
@@ -292,7 +292,6 @@
292292
<Class name="io.clientcore.core.serialization.xml.XmlSerializer" />
293293
<Class name="io.clientcore.core.utils.Base64Uri" />
294294
<Class name="io.clientcore.core.utils.CoreUtils" />
295-
<Class name="io.clientcore.core.utils.serializers.MockSerializer" />
296295
</Or>
297296
</Match>
298297
<Match>
@@ -405,7 +404,7 @@
405404
<Class name="io.clientcore.core.models.binarydata.BinaryDataTest$BinaryDataAsProperty" />
406405
<Class name="io.clientcore.core.serialization.xml.DefaultXmlWriterContractTests" />
407406
<Class name="io.clientcore.core.serialization.xml.implementation.aalto.in.ReaderConfig$EncodingContext" />
408-
<Class name="io.clientcore.core.utils.serializers.JsonSerializerTests$DateTimeWrapper" />
407+
<Class name="io.clientcore.core.serialization.json.JsonSerializerTests$DateTimeWrapper" />
409408
</Or>
410409
</Match>
411410
<Match>

sdk/clientcore/core/src/main/java/io/clientcore/core/instrumentation/logging/ExceptionLoggingEvent.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ public ExceptionLoggingEvent addKeyValue(String key, long value) {
6666
return this;
6767
}
6868

69+
/**
70+
* Adds a key with an Object value to the context of current log
71+
* and exception being created.
72+
*
73+
* @param key Key to associate the provided {@code value} with.
74+
* @param value The object value.
75+
* @return The updated {@link ExceptionLoggingEvent} object.
76+
* @see LoggingEvent#addKeyValue(String, Object)
77+
*/
78+
public ExceptionLoggingEvent addKeyValue(String key, Object value) {
79+
log.addKeyValue(key, value);
80+
return this;
81+
}
82+
6983
/**
7084
* Sets operation context on the log event being created.
7185
* It's used to correlate logs between each other and with other telemetry items.

sdk/clientcore/core/src/main/java/io/clientcore/core/serialization/json/JsonSerializer.java

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
package io.clientcore.core.serialization.json;
55

6+
import io.clientcore.core.implementation.ReflectionUtils;
7+
import io.clientcore.core.implementation.ReflectiveInvoker;
68
import io.clientcore.core.implementation.TypeUtil;
79
import io.clientcore.core.instrumentation.logging.ClientLogger;
10+
import io.clientcore.core.models.binarydata.BinaryData;
811
import io.clientcore.core.serialization.ObjectSerializer;
912
import io.clientcore.core.serialization.SerializationFormat;
1013

@@ -13,6 +16,7 @@
1316
import java.io.InputStream;
1417
import java.io.OutputStream;
1518
import java.lang.reflect.InvocationTargetException;
19+
import java.lang.reflect.Method;
1620
import java.lang.reflect.ParameterizedType;
1721
import java.lang.reflect.Type;
1822
import java.util.List;
@@ -64,18 +68,12 @@ public <T> T deserializeFromBytes(byte[] bytes, Type type) throws IOException {
6468
if (type instanceof ParameterizedType && List.class.isAssignableFrom(TypeUtil.getRawClass(type))) {
6569
ParameterizedType parameterizedType = (ParameterizedType) type;
6670
Type listElementType = parameterizedType.getActualTypeArguments()[0];
67-
if (listElementType instanceof Class<?>
68-
&& JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(listElementType))) {
69-
List<?> list = jsonReader.readArray(arrayReader -> {
70-
Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
71-
Class<?> clazz = (Class<?>) actualTypeArgument;
72-
try {
73-
return clazz.getMethod("fromJson", JsonReader.class).invoke(null, arrayReader);
74-
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
75-
throw LOGGER.throwableAtError().log(e, RuntimeException::new);
76-
}
77-
});
78-
return (T) list;
71+
if (JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(listElementType))) {
72+
return deserializeListOfJsonSerializables(jsonReader, parameterizedType);
73+
} else if (BinaryData.class.isAssignableFrom(TypeUtil.getRawClass(listElementType))) {
74+
return deserializeListOfBinaryData(jsonReader);
75+
} else {
76+
return (T) jsonReader.readUntyped();
7977
}
8078
} else if (type instanceof Class<?>
8179
&& JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) {
@@ -89,6 +87,40 @@ public <T> T deserializeFromBytes(byte[] bytes, Type type) throws IOException {
8987
}
9088
}
9189

90+
@SuppressWarnings("unchecked")
91+
private <T> T deserializeListOfBinaryData(JsonReader jsonReader) throws IOException {
92+
return (T) jsonReader.readArray(arrayReader -> BinaryData.fromObject(arrayReader.readUntyped()));
93+
}
94+
95+
@SuppressWarnings("unchecked")
96+
private <T> T deserializeListOfJsonSerializables(JsonReader jsonReader, ParameterizedType parameterizedType)
97+
throws IOException {
98+
Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
99+
Class<?> listElementClass = (Class<?>) actualTypeArgument;
100+
ReflectiveInvoker methodInvoker;
101+
try {
102+
103+
Method fromJson = listElementClass.getDeclaredMethod("fromJson", JsonReader.class);
104+
fromJson.setAccessible(true);
105+
methodInvoker = ReflectionUtils.getMethodInvoker(listElementClass, fromJson);
106+
} catch (Exception e) {
107+
throw LOGGER.throwableAtError().log(e, RuntimeException::new);
108+
}
109+
return (T) jsonReader.readArray(arrayReader -> {
110+
try {
111+
return methodInvoker.invoke(arrayReader);
112+
} catch (Throwable e) {
113+
if (e instanceof Error) {
114+
throw (Error) LOGGER.throwableAtError().log(message -> e);
115+
} else if (e instanceof IOException) {
116+
throw (IOException) LOGGER.throwableAtError().log(message -> e);
117+
} else {
118+
throw LOGGER.throwableAtError().log(e, RuntimeException::new);
119+
}
120+
}
121+
});
122+
}
123+
92124
/**
93125
* Reads a JSON stream into its object representation.
94126
*

sdk/clientcore/core/src/main/java/io/clientcore/core/serialization/json/JsonWriter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package io.clientcore.core.serialization.json;
55

6+
import io.clientcore.core.models.binarydata.BinaryData;
67
import io.clientcore.core.serialization.json.implementation.jackson.core.JsonFactory;
78
import io.clientcore.core.serialization.json.implementation.jackson.core.JsonGenerator;
89
import io.clientcore.core.utils.IOExceptionCheckedBiConsumer;
@@ -1322,6 +1323,9 @@ public JsonWriter writeUntyped(Object value) throws IOException {
13221323
return writeString(String.valueOf(((Character) value).charValue()));
13231324
} else if (value instanceof JsonSerializable<?>) {
13241325
return ((JsonSerializable<?>) value).toJson(this);
1326+
} else if (value instanceof BinaryData) {
1327+
((BinaryData) value).writeTo(this);
1328+
return this;
13251329
} else if (value instanceof Object[]) {
13261330
writeStartArray();
13271331
for (Object element : (Object[]) value) {

sdk/clientcore/core/src/test/java/io/clientcore/core/utils/serializers/JsonSerializerTests.java renamed to sdk/clientcore/core/src/test/java/io/clientcore/core/serialization/json/JsonSerializerTests.java

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
package io.clientcore.core.utils.serializers;
4+
package io.clientcore.core.serialization.json;
55

66
import io.clientcore.core.http.models.HttpMethod;
77
import io.clientcore.core.implementation.AccessibleByteArrayOutputStream;
88
import io.clientcore.core.implementation.TypeUtil;
9+
import io.clientcore.core.models.Person;
910
import io.clientcore.core.models.SimpleClass;
10-
import io.clientcore.core.serialization.json.JsonReader;
11-
import io.clientcore.core.serialization.json.JsonSerializable;
12-
import io.clientcore.core.serialization.json.JsonSerializer;
13-
import io.clientcore.core.serialization.json.JsonToken;
14-
import io.clientcore.core.serialization.json.JsonWriter;
11+
import io.clientcore.core.models.binarydata.BinaryData;
1512
import org.junit.jupiter.api.Test;
1613
import org.junit.jupiter.params.ParameterizedTest;
1714
import org.junit.jupiter.params.provider.Arguments;
@@ -27,6 +24,7 @@
2724
import java.nio.charset.StandardCharsets;
2825
import java.time.OffsetDateTime;
2926
import java.time.ZoneOffset;
27+
import java.util.Arrays;
3028
import java.util.Collections;
3129
import java.util.HashMap;
3230
import java.util.LinkedHashMap;
@@ -196,6 +194,55 @@ public void textToOutputStreamSerialization(Object value, String expected) throw
196194
}
197195
}
198196

197+
@ParameterizedTest
198+
@MethodSource("binaryDataListSerializationSupplier")
199+
public void testBinaryDataListSerialization(List<BinaryData> list, String expected) throws IOException {
200+
byte[] bytes = SERIALIZER.serializeToBytes(list);
201+
assertEquals(expected, new String(bytes, StandardCharsets.UTF_8));
202+
}
203+
204+
private static Stream<Arguments> binaryDataListSerializationSupplier() {
205+
return Stream.of(
206+
Arguments.of(Arrays.asList(BinaryData.fromString("hello"), BinaryData.fromObject(5)), "[\"hello\",5]"),
207+
Arguments.of(
208+
Arrays.asList(BinaryData.fromString("hello"), BinaryData.fromObject(5),
209+
BinaryData.fromObject(new Person().setAge(3).setName("John"))),
210+
"[\"hello\",5,{\"name\":\"John\",\"age\":3}]"));
211+
}
212+
213+
@Test
214+
public void testBinaryDataListDeserialization() throws IOException {
215+
byte[] bytes = "[\"hello\", 5, {\"name\":\"John\",\"age\":3}]".getBytes(StandardCharsets.UTF_8);
216+
217+
ParameterizedType type = TypeUtil.createParameterizedType(List.class, BinaryData.class);
218+
219+
List<BinaryData> binaryDataList = SERIALIZER.deserializeFromBytes(bytes, type);
220+
assertNotNull(binaryDataList);
221+
assertEquals(3, binaryDataList.size());
222+
assertTrue(binaryDataList.get(0) instanceof BinaryData);
223+
assertTrue(binaryDataList.get(1) instanceof BinaryData);
224+
assertTrue(binaryDataList.get(2) instanceof BinaryData);
225+
226+
assertEquals("hello", binaryDataList.get(0).toObject(String.class));
227+
assertEquals(5, (int) binaryDataList.get(1).toObject(Integer.class));
228+
229+
Person person = binaryDataList.get(2).toObject(Person.class);
230+
assertEquals("John", person.getName());
231+
assertEquals(3, person.getAge());
232+
}
233+
234+
@ParameterizedTest
235+
@MethodSource("binaryDataSerializationSupplier")
236+
public void testBinaryDataSerialization(BinaryData binaryData, String expected) throws IOException {
237+
assertEquals(expected, new String(SERIALIZER.serializeToBytes(binaryData)));
238+
}
239+
240+
private static Stream<Arguments> binaryDataSerializationSupplier() {
241+
return Stream.of(Arguments.of(BinaryData.fromObject(5), "5"), Arguments.of(BinaryData.fromObject("1"), "\"1\""),
242+
Arguments.of(BinaryData.fromString("3"), "\"3\""), Arguments
243+
.of(BinaryData.fromObject(new Person().setAge(3).setName("John")), "{\"name\":\"John\",\"age\":3}"));
244+
}
245+
199246
private static Stream<Arguments> textSerializationSupplier() {
200247
Map<String, String> map = Collections.singletonMap("key", "value");
201248

sdk/clientcore/core/src/test/java/io/clientcore/core/utils/serializers/MockSerializer.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)