-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Binarydata api update - Default json serializer #16319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
d09de6c
138b2a5
a70876a
de98cc4
5047c7a
ed4edd8
af836ce
92a611b
5bcd678
7ee660a
6d5e3f5
689fb25
62e41b0
8b56472
6dee1b4
dcd5457
7e12c49
d611072
6576ab5
09c4632
9be5791
7ed8f7d
32a3ef3
2671255
a8b5273
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,9 @@ | |
|
|
||
| import com.azure.core.util.FluxUtil; | ||
| import com.azure.core.util.logging.ClientLogger; | ||
| import com.azure.core.util.serializer.JsonSerializerProviders; | ||
| import com.azure.core.util.serializer.ObjectSerializer; | ||
| import com.azure.core.util.serializer.JsonSerializer; | ||
| import com.azure.core.util.serializer.TypeReference; | ||
| import static com.azure.core.util.FluxUtil.monoError; | ||
|
|
||
|
|
@@ -18,18 +20,26 @@ | |
| import java.io.InputStream; | ||
| import java.io.UncheckedIOException; | ||
| import java.nio.ByteBuffer; | ||
| import java.nio.charset.Charset; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.Arrays; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * This class is an abstraction over many different ways that binary data can be represented. The {@link BinaryData} | ||
| * can be created from {@link InputStream}, {@link Flux} of {@link ByteBuffer}, {@link String}, {@link Object} and byte | ||
| * array. The data is collected from provided sources and stored into a byte array. | ||
| * This class is an abstraction over many different ways that binary data can be represented. The data represented by | ||
| * {@link BinaryData} is immutable. The {@link BinaryData} can be created from {@link InputStream}, {@link Flux} of | ||
| * {@link ByteBuffer}, {@link String}, {@link Object}, or byte array. | ||
| * <p> | ||
| * It also provides a way to serialize and deserialize an {@link Object} into {@link BinaryData} given an | ||
| * {@link ObjectSerializer}. Code samples are explained below. | ||
| * It provides a way to serialize {@link Object} into {@link BinaryData} using API | ||
| * {@link BinaryData#fromObject(Object, ObjectSerializer)} where you can provide your {@link ObjectSerializer}. | ||
| * <p> | ||
| * It provides a way to de-serialize {@link BinaryData} into specified {@link Object} using API | ||
| * {@link BinaryData#toObject(Class, ObjectSerializer)} where you can provide object type and your | ||
| * {@link ObjectSerializer}. | ||
| * <p> | ||
| * It provides API to use default json serializer which is available in classpath. The serializer must implement | ||
| * {@link JsonSerializer} interface. | ||
| * <p> | ||
| * Code samples are explained below. | ||
|
hemanttanwar marked this conversation as resolved.
Outdated
|
||
| * | ||
| * <p><strong>Create an instance from Bytes</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#bytes} | ||
|
|
@@ -43,19 +53,32 @@ | |
| * <p><strong>Get an Object from {@link BinaryData}</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.to#ObjectAsync} | ||
| * | ||
| * <p><strong>Create an instance from Object</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Object} | ||
| * | ||
| * @see ObjectSerializer | ||
|
hemanttanwar marked this conversation as resolved.
|
||
| */ | ||
| public final class BinaryData { | ||
| private static final ClientLogger LOGGER = new ClientLogger(BinaryData.class); | ||
| private static final byte[] EMPTY_BYTES = new byte[0]; | ||
| private static final BinaryData EMPTY_DATA = new BinaryData(new byte[0]); | ||
|
|
||
| private static final Object LOCK = new Object(); | ||
|
|
||
| private final byte[] data; | ||
|
|
||
| private static volatile JsonSerializer defaultJsonSerializer; | ||
|
hemanttanwar marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * Create instance of {@link BinaryData} given the data. | ||
| * Create an instance of {@link BinaryData} from the given data. If {@code null} value is provided , it will be | ||
| * converted into empty byte array. | ||
| * | ||
| * @param data to represent as bytes. | ||
| * @throws NullPointerException If {@code data} is null. | ||
| */ | ||
| BinaryData(byte[] data) { | ||
| Objects.requireNonNull(data, "'data' cannot be null."); | ||
| if (Objects.isNull(data) || data.length == 0) { | ||
| data = EMPTY_BYTES; | ||
| } | ||
| this.data = Arrays.copyOf(data, data.length); | ||
| } | ||
|
|
||
|
|
@@ -72,24 +95,15 @@ public InputStream toStream() { | |
| } | ||
|
|
||
| /** | ||
| * Provides {@link Mono} of {@link InputStream} for the data represented by this {@link BinaryData} object. | ||
| * | ||
| * @return {@link InputStream} representation of the {@link BinaryData}. | ||
| */ | ||
| public Mono<InputStream> toStreamAsync() { | ||
| return Mono.fromCallable(() -> toStream()); | ||
| } | ||
|
|
||
| /** | ||
| * Create {@link BinaryData} instance with given {@link InputStream} as source of data. The {@link InputStream} is | ||
| * not closed by this function. | ||
| * Creates a {@link BinaryData} instance with given {@link InputStream} as source of data. The {@link InputStream} | ||
| * is not closed by this function. | ||
| * | ||
| * <p><strong>Create an instance from InputStream</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Stream} | ||
| * | ||
| * @param inputStream to read bytes from. | ||
| * @throws UncheckedIOException If any error in reading from {@link InputStream}. | ||
| * @throws NullPointerException if {@code inputStream} is null. | ||
| * @throws NullPointerException If {@code inputStream} is null. | ||
|
hemanttanwar marked this conversation as resolved.
|
||
| * @return {@link BinaryData} representing the binary data. | ||
| */ | ||
| public static BinaryData fromStream(InputStream inputStream) { | ||
|
|
@@ -111,11 +125,11 @@ public static BinaryData fromStream(InputStream inputStream) { | |
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we not able to handle this in a more efficient manner? As it stands this means that the entire input stream is read into a buffer, converted into a byte array, and then copied into a new byte array? At the very least you can avoid the byte array copy.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the last byte array and same optimization in other from..() API as well. |
||
|
|
||
| /** | ||
| * Asynchronously create {@link BinaryData} instance with given {@link InputStream} as source of data. The | ||
| * Asynchronously create a {@link BinaryData} instance with given {@link InputStream} as source of data. The | ||
| * {@link InputStream} is not closed by this function. | ||
| * | ||
| * @param inputStream to read bytes from. | ||
| * @throws NullPointerException if {@code inputStream} is null. | ||
| * @throws NullPointerException If {@code inputStream} is null. | ||
| * @return {@link Mono} of {@link BinaryData} representing the binary data. | ||
| */ | ||
| public static Mono<BinaryData> fromStreamAsync(InputStream inputStream) { | ||
|
|
@@ -125,14 +139,14 @@ public static Mono<BinaryData> fromStreamAsync(InputStream inputStream) { | |
| } | ||
|
|
||
| /** | ||
| * Create {@link BinaryData} instance with given {@link Flux} of {@link ByteBuffer} as source of data. It will | ||
| * Creates a {@link BinaryData} instance with given {@link Flux} of {@link ByteBuffer} as source of data. It will | ||
| * collect all the bytes from {@link ByteBuffer} into {@link BinaryData}. | ||
| * | ||
| * <p><strong>Create an instance from String</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Flux} | ||
| * | ||
| * @param data to use. | ||
| * @throws NullPointerException if {@code inputStream} is null. | ||
| * @throws NullPointerException If {@code data} is null. | ||
| * @return {@link Mono} of {@link BinaryData} representing binary data. | ||
| */ | ||
| public static Mono<BinaryData> fromFlux(Flux<ByteBuffer> data) { | ||
|
|
@@ -145,38 +159,24 @@ public static Mono<BinaryData> fromFlux(Flux<ByteBuffer> data) { | |
| } | ||
|
|
||
| /** | ||
| * Create {@link BinaryData} instance with given data and character set. | ||
| * | ||
| * <p><strong>Create an instance from String</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#String} | ||
| * Creates a {@link BinaryData} instance with given data. The {@link String} is converted into bytes using UTF_8 | ||
| * character set. If the String is {@code null}, an empty {@link BinaryData} will be returned. | ||
| * | ||
| * @param data to use. | ||
| * @param charSet to use. | ||
| * @throws NullPointerException if {@code inputStream} is null. | ||
| * @return {@link BinaryData} representing the binary data. | ||
| */ | ||
| public static BinaryData fromString(String data, Charset charSet) { | ||
| Objects.requireNonNull(data, "'data' cannot be null."); | ||
|
|
||
| return new BinaryData(data.getBytes(charSet)); | ||
| } | ||
|
|
||
| /** | ||
| * Create {@link BinaryData} instance with given data. The {@link String} is converted into bytes using | ||
| * {@link StandardCharsets#UTF_8} character set. | ||
| * | ||
| * @param data to use. | ||
| * @throws NullPointerException if {@code inputStream} is null. | ||
| * @return {@link BinaryData} representing binary data. | ||
| */ | ||
| public static BinaryData fromString(String data) { | ||
| Objects.requireNonNull(data, "'data' cannot be null."); | ||
| if (Objects.isNull(data) || data.length() == 0) { | ||
| return EMPTY_DATA; | ||
| } else { | ||
| return new BinaryData(data.getBytes(StandardCharsets.UTF_8)); | ||
|
hemanttanwar marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return new BinaryData(data.getBytes(StandardCharsets.UTF_8)); | ||
| } | ||
|
|
||
| /** | ||
| * Create {@link BinaryData} instance with given byte array data. | ||
| * Creates a {@link BinaryData} instance with given byte array data. If the byte array is {@code null}, an empty | ||
| * {@link BinaryData} will be returned. | ||
| * | ||
| * @param data to use. | ||
| * @return {@link BinaryData} representing the binary data. | ||
|
|
@@ -185,16 +185,44 @@ public static BinaryData fromBytes(byte[] data) { | |
| return new BinaryData(data); | ||
| } | ||
|
|
||
| /** | ||
| * Serialize the given {@link Object} into {@link BinaryData} using json serializer which is available on classpath. | ||
| * The serializer on classpath must implement {@link JsonSerializer} interface. If the given Object is {@code null}, | ||
| * an empty {@link BinaryData} will be returned. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This documentation should link through an aka.ms link to the appropriate json serializer documentation (which will soon move to docs.microsoft.com, which is why in the meanwhile we will use an aka.ms link)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added this link https://aka.ms/azsdk/java/wiki/serialization |
||
| * | ||
| * @param data The {@link Object} which needs to be serialized into bytes. | ||
| * @throws IllegalStateException If a {@link JsonSerializer} cannot be found on the classpath. | ||
| * @return {@link BinaryData} representing the JSON serialized object. | ||
| * | ||
| * @see JsonSerializer | ||
| */ | ||
| public static BinaryData fromObject(Object data) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given this API is converting into JSON we may want to call it
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we change it, we need to sync with .Net. I copied this API name from them. |
||
| if (Objects.isNull(data)) { | ||
| return EMPTY_DATA; | ||
| } | ||
| final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
| getDefaultSerializer().serialize(outputStream, data); | ||
|
|
||
| return new BinaryData(outputStream.toByteArray()); | ||
| } | ||
|
|
||
| /** | ||
| * Serialize the given {@link Object} into {@link BinaryData} using the provided {@link ObjectSerializer}. | ||
| * If the Object is {@code null}, an empty {@link BinaryData} will be returned. | ||
| * | ||
| * <p><strong>Create an instance from Object</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Object} | ||
| * | ||
| * @param data The {@link Object} which needs to be serialized into bytes. | ||
| * @param serializer to use for serializing the object. | ||
| * @throws NullPointerException if {@code inputStream} or {@code serializer} is null. | ||
| * @throws NullPointerException If {@code serializer} is null. | ||
| * @return {@link BinaryData} representing binary data. | ||
| */ | ||
| public static BinaryData fromObject(Object data, ObjectSerializer serializer) { | ||
| Objects.requireNonNull(data, "'data' cannot be null."); | ||
| if (Objects.isNull(data)) { | ||
| return EMPTY_DATA; | ||
| } | ||
|
|
||
| Objects.requireNonNull(serializer, "'serializer' cannot be null."); | ||
|
|
||
| final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
|
|
@@ -204,19 +232,19 @@ public static BinaryData fromObject(Object data, ObjectSerializer serializer) { | |
|
|
||
| /** | ||
| * Serialize the given {@link Object} into {@link Mono} {@link BinaryData} using the provided | ||
| * {@link ObjectSerializer}. | ||
| * {@link ObjectSerializer}. If the Object is {@code null}, an empty {@link BinaryData} will be returned. | ||
| * | ||
| * @param data The {@link Object} which needs to be serialized into bytes. | ||
| * @param serializer to use for serializing the object. | ||
| * @throws NullPointerException if {@code inputStream} or {@code serializer} is null. | ||
| * @throws NullPointerException If {@code serializer} is null. | ||
| * @return {@link Mono} of {@link BinaryData} representing the binary data. | ||
| */ | ||
| public static Mono<BinaryData> fromObjectAsync(Object data, ObjectSerializer serializer) { | ||
| Objects.requireNonNull(data, "'data' cannot be null."); | ||
| Objects.requireNonNull(serializer, "'serializer' cannot be null."); | ||
|
|
||
| if (Objects.isNull(serializer)) { | ||
| return monoError(LOGGER, new NullPointerException("'serializer' cannot be null.")); | ||
| } | ||
| return Mono.fromCallable(() -> fromObject(data, serializer)); | ||
|
|
||
| } | ||
|
|
||
| /** | ||
|
|
@@ -230,31 +258,22 @@ public byte[] toBytes() { | |
|
|
||
| /** | ||
| * Provides {@link String} representation of this {@link BinaryData} object. The bytes are converted into | ||
| * {@link String} using {@link StandardCharsets#UTF_8} character set. | ||
| * {@link String} using the UTF-8 character set. | ||
| * | ||
| * @return {@link String} representation of the data. | ||
| */ | ||
| public String toString() { | ||
| return new String(this.data, StandardCharsets.UTF_8); | ||
| } | ||
|
|
||
| /** | ||
| * Provides {@link String} representation of this {@link BinaryData} object given a character set. | ||
| * | ||
| * @param charSet to use to convert bytes into {@link String}. | ||
| * @return {@link String} representation of the the binary data. | ||
| */ | ||
| public String toString(Charset charSet) { | ||
| return new String(this.data, charSet); | ||
| } | ||
|
|
||
| /** | ||
| * Deserialize the bytes into the {@link Object} of given type by applying the provided {@link ObjectSerializer} on | ||
| * the data. | ||
| * | ||
| * @param clazz representing the type of the Object. | ||
| * @param serializer to use deserialize data into type. | ||
| * @param <T> Generic type that the data is deserialized into. | ||
| * @throws NullPointerException If {@code serializer} or {@code clazz} is null. | ||
| * @return The {@link Object} of given type after deserializing the bytes. | ||
| */ | ||
| public <T> T toObject(Class<T> clazz, ObjectSerializer serializer) { | ||
|
|
@@ -276,10 +295,65 @@ public <T> T toObject(Class<T> clazz, ObjectSerializer serializer) { | |
| * @param clazz representing the type of the Object. | ||
| * @param serializer to use deserialize data into type. | ||
| * @param <T> Generic type that the data is deserialized into. | ||
| * @throws NullPointerException if {@code clazz} or {@code serializer} is null. | ||
| * @throws NullPointerException If {@code clazz} or {@code serializer} is null. | ||
| * @return The {@link Object} of given type after deserializing the bytes. | ||
| */ | ||
| public <T> Mono<T> toObjectAsync(Class<T> clazz, ObjectSerializer serializer) { | ||
|
|
||
| if (Objects.isNull(clazz)) { | ||
| return monoError(LOGGER, new NullPointerException("'clazz' cannot be null.")); | ||
| } else if (Objects.isNull(serializer)) { | ||
| return monoError(LOGGER, new NullPointerException("'serializer' cannot be null.")); | ||
|
hemanttanwar marked this conversation as resolved.
|
||
| } | ||
| return Mono.fromCallable(() -> toObject(clazz, serializer)); | ||
| } | ||
|
|
||
| /** | ||
| * Deserialize the bytes into the {@link Object} of given type by using json serializer which is available in | ||
| * classpath. The serializer must implement {@link JsonSerializer} interface. A singleton instance of | ||
| * {@link JsonSerializer} is kept for this class to use. | ||
| * | ||
| * @param clazz representing the type of the Object. | ||
| * @param <T> Generic type that the data is deserialized into. | ||
| * @throws NullPointerException If {@code clazz} is null. | ||
| * @return The {@link Object} of given type after deserializing the bytes. | ||
| */ | ||
| public <T> T toObject(Class<T> clazz) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we rename this API to something JSON specific?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might force us, in future, to add other API for other formats.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JsonObject has a different meaning. So, I think |
||
| Objects.requireNonNull(clazz, "'clazz' cannot be null."); | ||
|
|
||
| TypeReference<T> ref = TypeReference.createInstance(clazz); | ||
| InputStream jsonStream = new ByteArrayInputStream(this.data); | ||
| return getDefaultSerializer().deserialize(jsonStream, ref); | ||
| } | ||
|
|
||
| /** | ||
| * Return a {@link Mono} by deserialize the bytes into the {@link Object} of given type after applying the Json | ||
|
hemanttanwar marked this conversation as resolved.
Outdated
|
||
| * serializer found on classpath. | ||
| * | ||
| * <p><strong>Gets the specified object</strong></p> | ||
| * {@codesnippet com.azure.core.experimental.util.BinaryDocument.to#ObjectAsync} | ||
| * | ||
| * @param clazz representing the type of the Object. | ||
| * @param <T> Generic type that the data is deserialized into. | ||
| * @throws NullPointerException If {@code clazz} is null. | ||
| * @return The {@link Object} of given type after deserializing the bytes. | ||
| */ | ||
| public <T> Mono<T> toObjectAsync(Class<T> clazz) { | ||
| if (Objects.isNull(clazz)) { | ||
| return monoError(LOGGER, new NullPointerException("'clazz' cannot be null.")); | ||
| } | ||
| return Mono.fromCallable(() -> toObject(clazz)); | ||
| } | ||
|
|
||
| /* This will ensure lazy instantiation to avoid hard dependency on Json Serializer. */ | ||
| private static JsonSerializer getDefaultSerializer() { | ||
| if (defaultJsonSerializer == null) { | ||
| synchronized (LOCK) { | ||
| if (defaultJsonSerializer == null) { | ||
| defaultJsonSerializer = JsonSerializerProviders.createInstance(); | ||
| } | ||
| } | ||
| } | ||
| return defaultJsonSerializer; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"The data represented by {@link BinaryData} is immutable" is an odd statement to make? It is taken into BinaryData and presumably copied, but when given back to the user is mutable again. Perhaps clarify what you mean by this statement.