Class Serialization
- Serialization
Serialization
The SDK uses Jackson for searializing and deserializing JSON.
The default configured ObjectMapper uses some modules to correctly work with our API.
The details can be found in JsonUtils.createObjectMapper(ModuleOptions)
Customization
To allow customization of the ObjectMapper the SDK uses ServiceLoader for
ModuleSupplier. Adding a file
resources/META-INF/services/io.vrap.rmf.base.client.utils.json.ModuleSupplier to your project
with FQCN of the module supplier to be used will register the supplied modules
E.g.:
io.vrap.rmf.base.client.utils.json.ModuleSupplier:com.commercetools.api.json.ApiModuleSupplier
ApiModule.java:package com.commercetools.api.json; import java.lang.reflect.InvocationTargetException; import java.util.Set; import com.commercetools.api.RegisterSimpleModuleMixin; import com.commercetools.api.SimpleModuleMixin; import com.fasterxml.jackson.databind.module.SimpleModule; import io.vrap.rmf.base.client.utils.json.modules.ModuleOptions; import org.reflections.Reflections; /** * Module to configure the default jackson {@link com.fasterxml.jackson.databind.ObjectMapper} e.g. to deserialize attributes and custom fields */ public class ApiModule extends SimpleModule { private static final long serialVersionUID = 0L; public ApiModule(ModuleOptions options) { Reflections reflections = new Reflections("com.commercetools"); Set<Class<?>> modules = reflections.getTypesAnnotatedWith(RegisterSimpleModuleMixin.class); for (Class<?> module : modules) { if (SimpleModuleMixin.class.isAssignableFrom(module)) { SimpleModuleMixin mixin; try { mixin = module.asSubclass(SimpleModuleMixin.class).getDeclaredConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } mixin.mixin(this, options); } } } }
Date and time attributes
When using Date, Time and DateTime types for product attributes or custom fields the SDK deserializes them as LocalDate,
LocalTime and ZonedDateTime. As sometimes it may needed to serialize them as String the ObjectMapper
can be configured with ModuleOptions. The ApiModule also is loading the configuration options
using System.getProperty(String) e.g.: commercetools.deserializeDateAttributeAsString
ApiModuleOptions options = ApiModuleOptions.of() .withDateAttributeAsString(true) .withDateCustomFieldAsString(true); ObjectMapper mapper = JsonUtils.createObjectMapper(options); ProjectApiRoot apiRoot = ApiRootBuilder.of() .withApiBaseUrl(ServiceRegion.GCP_EUROPE_WEST1.getApiUrl()) .withSerializer(ResponseSerializer.of(mapper)) .build("test"); ProductVariant variant = mapper.readValue(stringFromResource("attributes.json"), ProductVariant.class); assertThat(variant.getAttributes()).isNotEmpty(); Map<String, Attribute> attributes = variant.withProductVariant(AttributeAccessor::asMap); assertThat(attributes.get("date").getValue()).isInstanceOfSatisfying(String.class, localDate -> assertThat(localDate).isEqualTo("2020-01-01")); assertThat(attributes.get("time").getValue()).isInstanceOfSatisfying(String.class, localTime -> assertThat(localTime).isEqualTo("13:15:00.123")); assertThat(attributes.get("datetime").getValue()).isInstanceOfSatisfying(String.class, dateTime -> assertThat(dateTime).isEqualTo("2020-01-01T13:15:00.123Z")); assertThat(attributes.get("date").withAttribute(AttributeAccessor::asDate)) .isInstanceOfSatisfying(LocalDate.class, localDate -> assertThat(localDate).isEqualTo("2020-01-01")); assertThat(attributes.get("time").withAttribute(AttributeAccessor::asTime)) .isInstanceOfSatisfying(LocalTime.class, localTime -> assertThat(localTime).isEqualTo("13:15:00.123")); assertThat(attributes.get("datetime").withAttribute(AttributeAccessor::asDateTime)).isInstanceOfSatisfying( ZonedDateTime.class, dateTime -> assertThat(dateTime).isEqualTo("2020-01-01T13:15:00.123Z")); assertThat(attributes.get("set-date").getValue()).asList().first().isInstanceOf(String.class); assertThat(attributes.get("set-time").getValue()).asList().first().isInstanceOf(String.class); assertThat(attributes.get("set-datetime").getValue()).asList().first().isInstanceOf(String.class); assertThat(attributes.get("set-date").withAttribute(AttributeAccessor::asSetDate)).asList() .first() .isInstanceOf(LocalDate.class); assertThat(attributes.get("set-time").withAttribute(AttributeAccessor::asSetTime)).asList() .first() .isInstanceOf(LocalTime.class); assertThat(attributes.get("set-datetime").withAttribute(AttributeAccessor::asSetDateTime)).asList() .first() .isInstanceOf(ZonedDateTime.class);See the test code.
Number attributes
When using numbers for attributes and custom fields they will be automatically deserialized as Long if they don't have a fraction part.
This can be disabled with the options ApiModuleOptions.DESERIALIZE_ATTRIBUTE_NUMBER_AS_DOUBLE and ApiModuleOptions.DESERIALIZE_CUSTOM_FIELD_NUMBER_AS_DOUBLE
ApiModuleOptions options = ApiModuleOptions.of().withAttributeNumberAsDouble(true); ObjectMapper mapper = JsonUtils.createObjectMapper(options); ProductVariant variant = mapper.readValue(stringFromResource("attributes.json"), ProductVariant.class); assertThat(variant.getAttribute("integer").getValue()).isEqualTo(10.0);See the test code.
ApiModuleOptions options = ApiModuleOptions.of().withCustomFieldNumberAsDouble(true); ObjectMapper mapper = JsonUtils.createObjectMapper(options); CustomFields customFields = mapper.readValue(stringFromResource("customfields.json"), CustomFields.class); assertThat(customFields.getFields().values().get("integer")).isEqualTo(10.0);See the test code.
Deserialize Attributes as JsonNode
In case the automatic deserialization of attributes or custom fields is not needed you can set the option commercetools.deserializeAttributeAsJsonNode and/or
commercetools.deserializeCustomFieldAsJsonNode
ApiModuleOptions options = ApiModuleOptions.of().withCustomFieldAsJsonNode(true).withAttributeAsJsonNode(true); ObjectMapper mapper = JsonUtils.createObjectMapper(options); ProjectApiRoot apiRoot = ApiRootBuilder.of() .withApiBaseUrl(ServiceRegion.GCP_EUROPE_WEST1.getApiUrl()) .withSerializer(ResponseSerializer.of(mapper)) .build("test");See the test code.
-
Constructor Summary
Constructors -
Method Summary
-
Constructor Details
-
Serialization
public Serialization()
-