:toc: macro toc::[] = JSON http://en.wikipedia.org/wiki/JSON[JSON] (JavaScript Object Notation) is a popular format to represent and exchange data especially for modern web-clients. For mapping Java objects to JSON and vice-versa there is no official standard API. We use the established and powerful open-source solution http://wiki.fasterxml.com/JacksonHome[Jackson]. Due to problems with the wiki of fasterxml you should try this alternative link: https://github.com/FasterXML/jackson#jackson-project-home-github[Jackson/AltLink]. == Configure JSON Mapping In order to avoid polluting business objects with proprietary Jackson annotations (e.g. `@JsonTypeInfo`, `@JsonSubTypes`, `@JsonProperty`) we propose to create a separate configuration class. Every devonfw application (sample or any app created from our link:tutorial-newapp[app-template]) therefore has a class called `ApplicationObjectMapperFactory` that extends +ObjectMapperFactory+ from the +devon4j-rest+ module. It looks like this: [source,java] -------- @Named("ApplicationObjectMapperFactory") public class ApplicationObjectMapperFactory extends ObjectMapperFactory { public RestaurantObjectMapperFactory() { super(); // JSON configuration code goes here } } -------- == JSON and Inheritance If you are using inheritance for your objects mapped to JSON then polymorphism can not be supported out-of-the box. So in general avoid polymorphic objects in JSON mapping. However, this is not always possible. Have a look at the following example from our sample application: [[img-rest-inheritance]] .Transfer-Objects using Inheritance image::images/REST-Inheritance.png["inheritance class diagram",scaledwidth="80%",align="center"] Now assume you have a link:guide-service-layer#rest[REST service operation] as Java method that takes a +ProductEto+ as argument. As this is an abstract class the server needs to know the actual sub-class to instantiate. We typically do not want to specify the classname in the JSON as this should be an implementation detail and not part of the public JSON format (e.g. in case of a service interface). Therefore we use a symbolic name for each polymorphic subtype that is provided as virtual attribute +@type+ within the JSON data of the object: [source,json] -------- { "@type": "Drink", ... } -------- Therefore you add configuration code to the constructor of xref:configure-json-mapping[ApplicationObjectMapperFactory]. Here you can see an example from the sample application: [source,java] -------- setBaseClasses(ProductEto.class); addSubtypes(new NamedType(MealEto.class, "Meal"), new NamedType(DrinkEto.class, "Drink"), new NamedType(SideDishEto.class, "SideDish")); -------- We use `setBaseClasses` to register all top-level classes of polymorphic objects. Further we declare all concrete polymorphic sub-classes together with their symbolic name for the JSON format via `addSubtypes`. == Custom Mapping In order to map custom link:guide-datatype[datatypes] or other types that do not follow the Java bean conventions, you need to define a custom mapping. If you create objects dedicated for the JSON mapping you can easily avoid such situations. When this is not suitable follow these instructions to define the mapping: . As an example, the use of JSR354 (`javax.money`) is appreciated in order to process monetary amounts properly. However, without custom mapping, the default mapping of Jackson will produce the following JSON for a `MonetaryAmount`: + [source,json] ------ "currency": {"defaultFractionDigits":2, "numericCode":978, "currencyCode":"EUR"}, "monetaryContext": {...}, "number":6.99, "factory": {...} ------ + As clearly can be seen, the JSON contains too much information and reveals implementation secrets that do not belong here. Instead the JSON output expected and desired would be: + [source,json] ------ "currency":"EUR","amount":"6.99" ------ + Even worse, when we send the JSON data to the server, Jackson will see that `MonetaryAmount` is an interface and does not know how to instantiate it so the request will fail. Therefore we need a customized link:https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers[Serializer]. . We implement `MonetaryAmountJsonSerializer` to define how a `MonetaryAmount` is serialized to JSON: + [source,java] ------ public final class MonetaryAmountJsonSerializer extends JsonSerializer { public static final String NUMBER = "amount"; public static final String CURRENCY = "currency"; public void serialize(MonetaryAmount value, JsonGenerator jgen, SerializerProvider provider) throws ... { if (value != null) { jgen.writeStartObject(); jgen.writeFieldName(MonetaryAmountJsonSerializer.CURRENCY); jgen.writeString(value.getCurrency().getCurrencyCode()); jgen.writeFieldName(MonetaryAmountJsonSerializer.NUMBER); jgen.writeString(value.getNumber().toString()); jgen.writeEndObject(); } } ------ + For composite datatypes it is important to wrap the info as an object (`writeStartObject()` and `writeEndObject()`). `MonetaryAmount` provides the information we need by the `getCurrency()` and `getNumber()`. So that we can easily write them into the JSON data. . Next, we implement `MonetaryAmountJsonDeserializer` to define how a `MonetaryAmount` is deserialized back as Java object from JSON: + [source,java] ------ public final class MonetaryAmountJsonDeserializer extends AbstractJsonDeserializer { protected MonetaryAmount deserializeNode(JsonNode node) { BigDecimal number = getRequiredValue(node, MonetaryAmountJsonSerializer.NUMBER, BigDecimal.class); String currencyCode = getRequiredValue(node, MonetaryAmountJsonSerializer.CURRENCY, String.class); MonetaryAmount monetaryAmount = MonetaryAmounts.getAmountFactory().setNumber(number).setCurrency(currencyCode).create(); return monetaryAmount; } } ------ + For composite datatypes we extend from https://github.com/devonfw/devon4j/blob/develop/modules/rest/src/main/java/com/devonfw/module/rest/service/impl/json/AbstractJsonDeserializer.java[`AbstractJsonDeserializer`] as this makes our task easier. So we already get a `JsonNode` with the parsed payload of our datatype. Based on this API it is easy to retrieve individual fields from the payload without taking care of their order, etc. `AbstractJsonDeserializer` also provides methods such as `getRequiredValue` to read required fields and get them converted to the desired basis datatype. So we can easily read the amount and currency and construct an instance of `MonetaryAmount` via the official factory API. . Finally we need to register our custom (de)serializers with the following configuration code in the constructor of xref:configure-json-mapping[ApplicationObjectMapperFactory]:+ [source,java] -------- SimpleModule module = getExtensionModule(); module.addDeserializer(MonetaryAmount.class, new MonetaryAmountJsonDeserializer()); module.addSerializer(MonetaryAmount.class, new MonetaryAmountJsonSerializer()); -------- Now we can read and write `MonetaryAmount` from and to JSON as expected.