Azure Core offers the ObjectSerializer
and JsonSerializer
interfaces as a standard way to handle serialization and deserialization of specific serialization formats. There is a default implementation of these interfaces built into Azure Core, but users and Azure SDKs can specify their own through service provider interface (SPI). The SPI allows for the serialization layer to be plug-able and removes dependencies on given implementations. Here are the Azure Core models that support serializer customization
If you're a user looking to provide your serializer implementation, please refer to Custom JSON serializer.
If you're Azure Core developer and want to support customer serializers, use ObjectSerializer
abstraction and resolve instance with JsonSerializerProviders.createInstance (or AvroSerializerProviders)
com.azure.core.util.serializer.ObjectSerializer
- Generic interface for (de)serializing objects. Abstracts format-specific serializers.com.azure.core.util.serializer.JsonSerializer
- Generic interface covering basic JSON (de)serialization methods.com.azure.core.util.serializer.JsonSerializerProvider
- extension point - resolves JsonSerializer
implementation in runtime.com.azure.core.serializer.json.jackson.JacksonJsonSerializer
- JsonSerializer
implementation of the JsonSerializer
defaulting to Azure Core internal implementation.com.azure.core.serializer.json.gson.GsonJsonSerializer
- JsonSerializer
based on Gson. Intended for consumers.com.azure.core.serializer.avro.apache.ApacheAvroSerializer
- AvroSerializer
based on apache apache (de)serializer. Intended for consumers.All of them come with a builder and provider.
If you're a library developer and want users to be able to provide their own serializer - take a dependency on one of the implementation packages. We recommend using azure-core-serializer-json-jackson
for Json and azure-core-serializer-avro-apache
for Avro.
If you don't want users to be able to replace serializers and can't use the default one, you can create your implementation and instantiate it directly without calling into JsonSerializerProviders.createInstance
.
JsonSerializerProviders.createInstance(useDefaultIfAbsent)
.
useDefaultIfAbsent=false
, make sure to include a custom one (e.g. one in azure-core-serializer-json-jackson
) and configure itJsonSerializerProviders.createInstance
unless you fully replace the serializer.JacksonJsonSerializerBuilder.serializer
method to pass customized ObjectMapper
. Consumers won't be able to replace or customize this instance.Please refer to the reference docs for more details.
Supporting user-provided serializer instancepublic final class User { private static final JsonSerializer SERIALIZER = JsonSerializerProviders.createInstance(true); public User() {} @JsonProperty public String firstName; @JsonProperty public String lastName; public static User fromString(String str) { return SERIALIZER.deserializeFromBytes(str.getBytes(StandardCharsets.UTF_8), TypeReference.createInstance(User.class)); } public String toString() { return new String(SERIALIZER.serializeToBytes(this), StandardCharsets.UTF_8); } }Using JacksonJsonSerializerBuilder
If you don't need to support customizable serializers for the model, but want to configure some settings on the serializer, please use JacksonJsonSerializerBuilder
from azure-core-serializer-json-jackson
. A similar approach can be used with ApacheAvroSerializerBuilder
or GsonJsonSerializerBuilder
.
public final class User { private static final JsonSerializer SERIALIZER = new JacksonJsonSerializerBuilder() .serializer(new ObjectMapper().registerModule( new SimpleModule().addSerializer(User.class, new UserSerializer()) .addDeserializer(User.class, new UserDeserializer()))) .build(); public User() {} @JsonProperty public String firstName; @JsonProperty public String lastName; public static User fromString(String str) { return SERIALIZER.deserializeFromBytes(str.getBytes(StandardCharsets.UTF_8), TypeReference.createInstance(User.class)); } public String toString() { return new String(SERIALIZER.serializeToBytes(this), StandardCharsets.UTF_8); } }
If you're an Azure Core or Azure SDK developer and don't want customers to provide their own serializer, use SerializerAdapter abstraction and JacksonAdapter
implementation.
com.azure.core.util.serializer.SerializerAdapter
- interface for all serialization (Json and XML).com.azure.core.util.serializer.JacksonAdapter
- implements SerializerAdapter
- default (de)serialization (json and XML) implementation.JacksonAdapter()
constructor or use static createDefaultSerializerAdapter()
method that returns a singleton instance.JacksonAdapter.serializer()
returns underlying com.fasterxml.jackson.databind.ObjectMapper
so you may add or override default configuration. WARNING: please avoid changing configuration on the singleton instance returned by createDefaultSerializerAdapter
- Azure Core functionality depends on it.serializeRaw
, serializeList
, serializeIterable
, <T> deserialize(HttpHeaders headers, Type deserializedHeadersType)
only support Json.Here is the default configuration applied on json/xml serialization in Azure Core.
Applies to:
JacksonAdapter
- default implementation. Note: if there is no custom provider on the classpath, JsonSerializerProviders.createInstance(/*useDefaultIfAbsent*/ true)
resolves to serializer that wraps JacksonAdapter
, i.e. below configuration would apply.JacksonJsonSerializer
- (unless custom ObjectMapper
is used) with some exceptions mentioned below.Configuration does not apply to GsonJsonSerializer
, JacksonAvroSerializer
or ApacheAvroSerializer
- they use corresponding underlying package (jackson, gson, apache avro) defaults unless customized.
Note: Jackson behavior regarding null/empty (de)serialization edge cases depends on version and subject to change (especially in xml). Please support variety of cases and avoid depending on specific behavior.
JacksonJsonSerializer
uses Jackson defaults (unless customized).
JacksonAdapter
: noJacksonJsonSerializer
: yes, as null.<a></a>
)<a></a>
) - empty string<a/>
) - null<a></a>
) - null<a/>
) - nullnull
(map/iterable/list/array)null
(map/iterable/list/array)<a/>
) - null
(map/iterable/list/array) since Jackson 2.12.4<a/>
)
Iterable
is not serialized at all (TODO)<a></a>
), depends on type:
String
s: [""]
String
s: [null]
(because of DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
setting)String
s: null
<a/>
) - null
since Jackson 2.12.4<a>1</a>
<a>1</a>
: 1-element array<a></a>
), depends on type:
String
s: [""]
String
s: [null]
(because of DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
setting that works for arrays only)String
s: null
[1,2,3,4,5,6,7,8]
-> AQIDBAUGBwg=
Annotated properties/fields are always serialized regardless of visibility. For auto-detected fields/properties/etc JacksonAdapter
and JacksonJsonSerializer
have different settings. JacksonJsonSerializer
uses jackson defaults (unless customized).
Not-annotated fields:
JacksonAdapter
: serialized regardless of visibilityJacksonJsonSerializer
: public-only not-annotated fieldsNot-annotated properties (getter + setter):
JacksonAdapter
: noneJacksonJsonSerializer
: public-only getters, any settersNot-annotated Is-Getter
JacksonAdapter
: noneJacksonJsonSerializer
: public-onlyCreators: public-only.
{}
)JacksonAdapter
: Jackson exceptions are not handled and not logged.JacksonJsonSerializer
: all IOException
s (including MismatchedInputException
, JackMappingException
and JacksonProcessingException
) are logged and re-thrown wrapped into UncheckedIOException
.Json-specific. Not supported in XML.
additionalProperties
is a magic word that allows to serialize map with String keys as top-level properties.
Please use @JsonAnyGetter
/@JsonAnySetter
instead (if you can) for performance reasons.
@JsonProperty
(auto-detection does not work)KeySerializer
is provided.additionalProperties
as long as value type allows that (and throws otherwise)@JsonAnyGetter
/@JsonAnySetter
additionalProperties
are Azure SDK concept, @JsonAnyGetter
/@JsonAnySetter
is jackson annotations. Please use @JsonAnyGetter
/@JsonAnySetter
when possible. Usage of additionalProperties
adds extra performance overhead (~10x) and is not recommended in perf-sensitive scenarios.
Flattening is Azure SDK concept that allows to write more compact models.
Class-level flattening example@JsonFlatten class Model { @JsonProperty("property.name") private String name = "foo"; @JsonProperty("property.value") private String value = "bar"; @JsonProperty("property\\.escaped") private String escaped = "baz"; }
translates into
{ "property" : { "name" : "foo", "value" : "bar" }, "property.escaped" : "baz" }Field-level flattening example
class Model { @JsonFlatten @JsonProperty("property.name") private String name = "foo"; @JsonFlatten @JsonProperty("property.value") private String value = "bar"; @JsonProperty("property.not.escaped") private String notEscaped = "baz"; }
translates into
{ "property" : { "name" : "foo", "value" : "bar" }, "property.not.escaped" : "baz" }
@JsonFlatten
annotation, no auto-detection.
are populated as nested fields.
in model definition are populated from nested nodes.
) are populated as nested fields
.
) in model definition are populated from nested nodes\.
is used if class is annotated with @JsonFlatten
and specific field does not need to be annotated.additionalProperties
(or @JsonAnyGetter
/@JsonAnySetter
) along with @JsonFlatten
on class or field levels is not supported (flattening is not possible).Duration
:
"P1DT10H17M36.789S"
), controlled by DurationSerializer
123456.789
- number of seconds with nano precisionJavaTimeModule
)
Instant
:
"2021-07-06T19:47:12.728012100Z"
), controlled by MapperBuilder.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
JavaTimeModule
)
OffsetDateTime
"2021-07-06T20:09:01.465447100Z"
), controlled by DateTimeSerializer
"2021-07-06T13:11:08.3230678-07:00"
, controlled by MapperBuilder.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
JavaTimeModule
)
UnixTime
1.625602953E9
), controlled by UnixTimeSerializer
UnixTime
not supported) - {"dateTime":"2021-07-06T20:20:15.193979500Z"}
and depends on JavaTimeModule
and WRITE_DATES_AS_TIMESTAMPS
DateTimeRfc1123
"Tue, 06 Jul 2021 20:31:19 GMT"
), controlled by DateTimeRfc1123Serializer
{"dateTime":"2021-07-06T20:20:15.193979500Z"}
and depends on JavaTimeModule
and WRITE_DATES_AS_TIMESTAMPS
ZonedDateTime
:
"2021-07-06T14:08:08.0519546-07:00"
(JavaTimeModule
and WRITE_DATES_AS_TIMESTAMPS
)JavaTimeModule
)
ZonedDateTime.parse(key, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
)Any few more with the same (de)serialization behavior as ZonedDateTime
:
LocalDateTime
- "2021-07-06T14:08:08.0389576"
LocalDate
- "2021-07-06"
LocalTime
- "14:08:08.0379605"
MonthDay
- "--07-06"
OffsetTime
- "14:08:08.050955100-07:00"
Period
- "P10D"
Year
- "2021"
YearMonth
- "2021-07"
ZoneId
- "-07:00"
ZoneOffset
- "-07:00"
Base64Url(String)
Serialized as Map<String, String>
. If a header has multiple values, they are joined in a comma-separated string. Using headers along with additionalProperties
(or @JsonAnyGetter
/@JsonAnySetter
) is not supported.
Following models are (de)serialized using serializer provided with JsonSerializerProvider
, defaulting to JacksonAdapter
which no custom serializer is provided:
CloudEvent
BinaryData
RequestContent
JsonPatchDocument
with the exception that it does not allow using default implementation (TODO is is a bug?)Consumers and Azure SDKs are encouraged to bring their own serializers to customize serialization for these models.
GeoObject
and its friends are pure models and don't handle their own (de)serialization. DynamicRequest
accepts any ObjectSerializer
implementation in constructor to serialize request body.
additionalProperties
and JsonFlatten
should not be used in performance-sensitive cases. They have overhead on each (de)serialization call. Please consider adjusting models for data-plane scenarios. We assume they are used in management SDKs and management SDKs are less sensitive to perf.RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4