General data-binding package for Jackson: works on streaming API (core) implementation(s)
General data-binding package for Jackson: works on streaming API (core) implementation(s)
This project contains the general-purpose data-binding functionality and tree-model for Jackson Data Processor. It builds on Streaming API (stream parser/generator) package, and uses Jackson Annotations for configuration. Project is licensed under Apache License 2.0.
While the original use case for Jackson was JSON data-binding, it can now be used to read content encoded in other data formats as well, as long as parser and generator implementations exist. Naming of classes uses word 'JSON' in many places even though there is no actual hard dependency to JSON format.
Functionality of this package is contained in Java package tools.jackson.databind (for Jackson 3.x), and can be used using following Maven dependency:
<properties>
...
<jackson.version>3.0.0</jackson.version>
...
</properties>
<dependencies>
...
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
...
</dependencies>
Package also depends on jackson-core and jackson-annotations packages, but when using build tools
like Maven or Gradle, dependencies are automatically included.
You may, however, want to use jackson-bom to ensure compatible versions
of dependencies.
If not using build tool that can handle dependencies using project's pom.xml, you will need to download
and include these 2 jars explicitly.
For use cases that do not automatically resolve dependencies from Maven repositories, you can still download jars from Central Maven repository.
Databind jar is also a functional OSGi bundle, with proper import/export declarations, so it can be use on OSGi container as is.
Jackson 2.10 and above include module-info.class definitions so the jar is also a proper Java Module (JPMS).
Jackson 2.12 and above include additional Gradle 6 Module Metadata for version alignment with Gradle.
Jackson-databind package baseline JDK requirements are as follows:
List is incomplete due to compatibility checker addition being done for Jackson 2.13.
for information on Android SDK versions to Android Release names see [https://en.wikipedia.org/wiki/Android_version_history]
More comprehensive documentation can be found from Jackson-docs repository; as well as from Wiki of this project. But here are brief introductionary tutorials, in recommended order of reading.
The most common usage is to take piece of JSON, and construct a Plain Old Java Object ("POJO") out of it. So let's start there. With simple 2-property POJO like this:
// Note: can use getters/setters as well; here we just use public fields directly:
public class MyValue {
public String name;
public int age;
// NOTE: if using getters/setters, can keep fields `protected` or `private`
}
we will need a tools.jackson.databind.ObjectMapper instance, used for all data-binding, so let's construct one:
// With default settings can use
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
// But if configuration needed, use builder pattern:
ObjectMapper mapper = JsonMapper.builder()
// configuration
.build();
The default instance is fine for our use -- we will learn later on how to configure mapper instance if necessary. Usage is simple:
MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
// or:
value = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);
// or:
value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
And if we want to write JSON, we do the reverse:
mapper.writeValue(new File("result.json"), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);
So far so good?
Beyond dealing with simple Bean-style POJOs, you can also handle JDK Lists, Maps:
Map<String, Integer> scoreByName = mapper.readValue(jsonSource, Map.class);
List<String> names = mapper.readValue(jsonSource, List.class);
// and can obviously write out as well
mapper.writeValue(new File("names.json"), names);
as long as JSON structure matches, and types are simple.
If you have POJO values, you need to indicate actual type (note: this is NOT needed for POJO properties with List etc types):
Map<String, ResultValue> results = mapper.readValue(jsonSource,
new TypeReference<Map<String, ResultValue>>() { } );
// why extra work? Java Type Erasure will prevent type detection otherwise
(note: no extra effort needed for serialization, regardless of generic types)
But wait! There is more!
(enters Tree Model...)
While dealing with Maps, Lists and other "simple" Object types (Strings, Numbers, Booleans) can be simple, Object traversal can be cumbersome.
This is where Jackson's Tree model can come in handy:
…
Tree Model can be more convenient than data-binding, especially in cases where structure is highly dynamic, or does not map nicely to Java classes.
Finally, feel free to mix and match, and even in the same json document (useful when only part of the document is known and modeled in your code)
…
Supported for Jackson 2.16+ versions
// generics
List<Person> friends = mapper.treeToValue(root.get("friends"), new TypeReference<List<Person>>() { });
// create as you go but without trying json path
root.withObjectProperty("deep").withObjectProperty("large").withObjectProperty("hiearchy").put("important", 42);
As convenient as data-binding (to/from POJOs) can be; and as flexible as Tree model can be, there is one more canonical processing model available: incremental (aka "streaming") model. It is the underlying processing model that data-binding and Tree Model both build upon, but it is also exposed to users who want ultimate performance and/or control over parsing or generation details.
For in-depth explanation, look at Jackson Core component. But let's look at a simple teaser to whet your appetite.
…
There are two entry-level configuration mechanisms you are likely to use: Features and Annotations.
Here are examples of configuration features that you are most likely to need to know about.
Let's start with higher-level data-binding configuration.
With Jackson 3.x, you need to use "Builder"-style construction (2.x also supported direct configuration but this was removed to make ObjectMapper instances immutable and fully thread-safe)
…
In addition, you may need to change some of low-level JSON parsing, generation details. This happens by enabling disabling:
StreamReadFeature / StreamWriteFeature for generic (format-agnostic) settingsJsonReadFeature / JsonWriteFeature for JSON-specific settings…
Full set of features are explained on Jackson Features page.
The simplest annotation-based approach is to use @JsonProperty annotation like so:
public class MyBean {
private String _name;
// without annotation, we'd get "theName", but we want "name":
@JsonProperty("name")
public String getTheName() { return _name; }
// note: it is enough to add annotation on just getter OR setter;
// so we can omit it here
public void setTheName(String n) { _name = n; }
}
There are other mechanisms to use for systematic naming changes, including use of "Naming Strategy" (via @JsonNaming annotation).
You can use Mix-in Annotations to associate any and all Jackson-provided annotations.
There are two main annotations that can be used to ignore properties: @JsonIgnore for individual properties; and @JsonIgnoreProperties for per-class definition
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped
// regardless of whether POJO has such properties
@JsonIgnoreProperties({ "foo", "bar" })
public class MyBean
{
// will not be written as JSON; nor assigned from JSON:
@JsonIgnore
public String internal;
// no annotation, public field is read/written normally
public String external;
@JsonIgnore
public void setCode(int c) { _code = c; }
// note: will also be ignored because setter has annotation!
public int getCode() { return _code; }
}
As with renaming, note that annotations are "shared" between matching fields, getters and setters: if only one has @JsonIgnore, it affects others.
But it is also possible to use "split" annotations, to for example:
public class ReadButDontWriteProps {
private String _name;
@JsonProperty public void setName(String n) { _name = n; }
@JsonIgnore public String getName() { return _name; }
}
in this case, no "name" property would be written out (since 'getter' is ignored); but if "name" property was found from JSON, it would be assigned to POJO property!
For a more complete explanation of all possible ways of ignoring properties when writing out JSON, check "Filtering properties" article.
Unlike many other data-binding packages, Jackson does not require you to define "default constructor" (constructor that does not take arguments). While it will use one if nothing else is available, you can easily define that an argument-taking constructor is used:
public class CtorBean
{
public final String name;
public final int age;
@JsonCreator // constructor can be public, private, whatever
private CtorBean(@JsonProperty("name") String name,
@JsonProperty("age") int age)
{
this.name = name;
this.age = age;
}
}
Constructors are especially useful in supporting use of Immutable objects.
Alternatively, you can also define "factory methods":
public class FactoryBean
{
// fields etc omitted for brevity
@JsonCreator
public static FactoryBean create(@JsonProperty("name") String name) {
// construct and return an instance
}
}
Note that use of a "creator met
`ArrayStoreException` for Builder-based type with Object Id when a forward reference in a typed array is resolved by a later element of the same array
`@JacksonInject(useInput = OptBoolean.FALSE)` ignored for Field and Setter properties
[GLD Serializer Benchmark] Java jackson 2.19.0 — spec compliance misses
Vanilla `BeanDeserializer` fast path never used with default settings in 3.x (due to `MapperFeature.DEFAULT_VIEW_INCLUSION` default change)
@JsonAlias ignored in enum if @JsonValue is present