Mapper
org.apache.johnzon:johnzon-mapper:2.2.0
Contents
johnzon-mapper maps Java objects to JSON and back, on top of whichever JSON Processing
implementation you choose:
final MySuperObject object = createObject();
try (Mapper mapper = new MapperBuilder().build()) {
mapper.writeObject(object, outputStream);
final MySuperObject otherObject = mapper.readObject(inputStream, MySuperObject.class);
}The mapper uses a direct Java-to-JSON representation. This bean:
public class MyModel {
private int id;
private String name;
// getters/setters
}maps to:
{
"id": 1234,
"name": "Johnzon doc"
}Customization happens either directly on the MapperBuilder or through annotations.
Annotations
@JohnzonIgnore
Ignores a field, optionally only up to some version when the mapper has one:
public class MyModel {
@JohnzonIgnore
private String name;
// getters/setters
}To serialize name from version 3 on but ignore it for versions 1 and 2:
public class MyModel {
@JohnzonIgnore(minVersion = 3)
private String name;
// getters/setters
}@JohnzonConverter
Converters handle advanced mapping between Java and JSON. The types are:
Converter: maps between Java and JSON based on the string representationAdapter: a converter not limited toStringObjectConverter.Reader: converts JSON to Java at a low levelObjectConverter.Writer: converts Java to JSON at a low levelObjectConverter.Codec: aReaderandWriterin one
The most common case is a custom date format, which a plain Converter covers:
public class LocalDateConverter implements Converter<LocalDate> {
@Override
public String toString(final LocalDate instance) {
return instance.toString();
}
@Override
public LocalDate fromString(final String text) {
return LocalDate.parse(text);
}
}When you need to modify the structure of the JSON itself (wrapping a value, for
instance), use a Reader/Writer or a Codec. Register converters globally on the
MapperBuilder, or decorate the field:
public class MyModel {
@JohnzonConverter(LocalDateConverter.class)
private LocalDate date;
// getters/setters
}Mapping java.lang.Class
ClassConverter is not registered by default: reading a java.lang.Class from a
document lets the document pick which class is loaded, so it must be registered
explicitly with the classes it is allowed to load. Both allow-lists (exact names and
name prefixes) default to empty, i.e. any incoming class name is rejected:
new MapperBuilder()
.addConverter(Class.class, new ClassConverter(
List.of("com.company.handler.Default"), // allowed class names
List.of("com.company.handler."))) // allowed class name prefixes
.build();@JohnzonProperty
When the JSON name is not Java-friendly (_foo, foo-bar, or even 200),
@JohnzonProperty sets the name to use:
public class MyModel {
@JohnzonProperty("__date")
private LocalDate date;
// getters/setters
}@JohnzonAny
When you don’t fully know your model but want to handle all keys, @JohnzonAny
captures and serializes the rest:
public class AnyMe {
private String name; // regular serialization for the known 'name' field
/* A TreeMap stores the unknown fields for the @JohnzonAny methods here,
but any storage works. @JohnzonIgnore keeps it from being exposed as
an actual 'unknownFields' property in JSON. */
@JohnzonIgnore
private Map<String, Object> unknownFields = new TreeMap<String, Object>();
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
@JohnzonAny
public Map<String, Object> getAny() {
return unknownFields;
}
@JohnzonAny
public void handle(final String key, final Object val) {
this.unknownFields.put(key, val);
}
}Access modes
MapperBuilder ships several access modes, selected with setAccessModeName():
field: uses fields and ignores getters/settersmethod: uses getters/setters (a getter without a setter serializes but never reads)strict-method: the POJO-convention based default. Likemethod, but collection getters are not used to writeboth: field and method accessors merged
You can also implement your own access mode.