Jackson - Quickstart
JSON Three Ways
Jackson offers three alternative methods (one with two variants) for processing JSON:
Streaming API (aka "Incremental parsing/generation") reads and writes JSON content as discrete events.
org.codehaus.jackson.JsonParser reads, org.codehaus.jackson.JsonGenerator writes.
Inspired by the StAX API.
Tree Model provides a mutable in-memory tree representation of a JSON document.
org.codehaus.jackson.map.ObjectMapper can build trees; trees consist of JsonNode nodes.
- The tree model is similar to the XML DOM.
Data Binding converts JSON to and from POJOs based either on property accessor conventions or annotations.
There are two variants: simple and full data binding
Simple data binding means converting to and from Java Maps, Lists, Strings, Numbers, Booleans and nulls
Full data binding means converting to and from any Java bean type (as well as "simple" types mentioned above)
org.codehaus.jackson.map.ObjectMapper performs the marshalling (writing JSON) and unmarshalling (reading JSON) for both variants.
Inspired by the annotation-based (code-first) variant of JAXB.
From usage perspective, one way to summarize these 3 methods is:
Streaming API is best performing (lowest overhead, fastest read/write; other 2 methods build on it)
Data Binding is often most convenient
Tree Model is most flexible
Given these properties, let's consider these in the reverse order, starting with what is usually the most natural and convenient method for Java developers: Jackson Data Binding API.
Examples
Full Data Binding (POJO) Example
Jackson's org.codehaus.jackson.map.ObjectMapper "just works" for mapping JSON data into plain old Java objects ("POJOs"). For example, given JSON data
{
"name" : { "first" : "Joe", "last" : "Sixpack" },
"gender" : "MALE",
"verified" : false,
"userImage" : "Rm9vYmFyIQ=="
}
It takes two lines of Java to turn it into a User instance:
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
User user = mapper.readValue(new File("user.json"), User.class);
Where the User class looks something like this:
public class User {
public enum Gender { MALE, FEMALE }; public static class Name {
private String _first, _last; public String getFirst() { return _first; }
public String getLast() { return _last; } public void setFirst(String s) { _first = s; }
public void setLast(String s) { _last = s; }
} private Gender _gender;
private Name _name;
private boolean _isVerified;
private byte[] _userImage; public Name getName() { return _name; }
public boolean isVerified() { return _isVerified; }
public Gender getGender() { return _gender; }
public byte[] getUserImage() { return _userImage; } public void setName(Name n) { _name = n; }
public void setVerified(boolean b) { _isVerified = b; }
public void setGender(Gender g) { _gender = g; }
public void setUserImage(byte[] b) { _userImage = b; }
}
Marshalling back to JSON is similarly straightforward:
mapper.writeValue(new File("user-modified.json"), user);
For fancier data binding (e.g., unmarshalling formatted dates into java.util.Date), Jackson provides annotations to customize the marshalling and unmarshalling process.
"Raw" Data Binding Example
(also known as "Untyped", or sometimes "simple" data binding)
In cases where you do not have (and don't want to create) specific Java classes to bind JSON to/from, "Untyped data binding" may be a better approach. It is used same way as full data binding, except that the formal binding type is specified simply as Object.class (or Map.class, List.class, String[].class if more specific typing is wanted). So the earlier binding of JSON that represent User data could have been done by:
Map<String,Object> userData = mapper.readValue(new File("user.json"), Map.class);
and userData would be like one we would explicit construct by:
Map<String,Object> userData = new HashMap<String,Object>();
Map<String,String> nameStruct = new HashMap<String,String>();
nameStruct.put("first", "Joe");
nameStruct.put("last", "Sixpack");
userData.put("name", nameStruct);
userData.put("gender", "MALE");
userData.put("verified", Boolean.FALSE);
userData.put("userImage", "Rm9vYmFyIQ==");
This obviously works both ways: if you did construct such a Map (or bind from JSON and modify), you could write out just as before, by:
mapper.writeValue(new File("user-modified.json"), userData);
How does this work? By specifying Map.class, we do not specify generic key/value types. But ObjectMapper does know how to bind JSON data to and from Maps (and Lists, arrays, wrapper types), and does just that. Fundamentally JSON data has no "real" type as far as Jackson is concerned -- if it can be properly mapped to a type you give, it will be mapped.
Concrete Java types that Jackson will use for simple data binding are:
JSON Type | Java Type |
object | LinkedHashMap<String,Object> |
array | ArrayList<Object> |
string | String |
number (no fraction ) | Integer, Long or BigInteger (smallest applicable) |
number (fraction) | Double (configurable to use BigDecimal) |
true|false | Boolean |
null | null |
Data Binding with Generics
In addition to binding to POJOs and "simple" types, there is one additional variant: that of binding to generic (typed) containers. This case requires special handling due to so-called Type Erasure (used by Java to implement generics in somewhat backwards compatible way), which prevents you from using something like Collection<String>.class (which does not compile).
So if you want to bind data into a Map<String,User> you will need to use:
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() { });
where TypeReference is only needed to pass generic type definition (via anynomous inner class in this case): the important part is <Map<String,User>> which defines type to bind to.
If you don't do this (and just pass Map.class), call is equivalent to binding to Map<?,?> (i.e. "untyped" Map), as explained above.
UPDATE: As an alternative, version 1.3 also allows programmatic construction of types by using TypeFactory.
Tree Model Example
Yet another way to get Objects out of JSON is to build a tree. This is similar to DOM trees for XML. The way Jackson builds trees is to use basic JsonNode base class, which exposes read access that is usually needed. Actual node types used are sub-classes; but the sub-type only needs to be used when modifying trees.
Trees can be read and written using either Streaming API (see below), or using ObjectMapper.
With ObjectMapper, you will do something like:
ObjectMapper m = new ObjectMapper();
// can either use mapper.readTree(source), or mapper.readValue(source, JsonNode.class);
JsonNode rootNode = m.readTree(new File("user.json"));
// ensure that "last name" isn't "Xmler"; if is, change to "Jsoner"
JsonNode nameNode = rootNode.path("name");
String lastName = nameNode.path("last").getTextValue().
if ("xmler".equalsIgnoreCase(lastName)) {
((ObjectNode)nameNode).put("last", "Jsoner");
}
// and write it out:
m.writeValue(new File("user-modified.json"), rootNode);
Or if you want to construct a Tree (for the User example) from scratch, you can do:
TreeMapper treeMapper = new TreeMapper();
ObjectNode userOb = treeMapper.objectNode();
Object nameOb = userRoot.putObject("name");
nameOb.put("first", "Joe");
nameOb.put("last", "Sixpack");
userOb.put("gender", User.Gender.MALE.toString());
userOb.put("verified", false);
byte[] imageData = getImageData(); // or wherever it comes from
userOb.put("userImage", imageData);
(NOTE: with Jackson 1.2 you can use ObjectMapper directly, using ObjectMapper.createObjectNode() to create userOb -- above example will work with JAckson 1.0 and 1.1)
Streaming API Example
And finally, there is the third way: turbo-charged, high-performance method known as Streaming API (aka incremental mode, since content is read and written incrementally).
Just for fun, let's implement the writing functionality (equivalent to earlier examples) using "raw" Streaming API: WriteJSON.java
JsonFactory f = new JsonFactory();
JsonGenerator g = f.createJsonGenerator(new File("user.json")); g.writeStartObject();
g.writeObjectFieldStart("name");
g.writeStringField("first", "Joe");
g.writeStringField("last", "Sixpack");
g.writeEndObject(); // for field 'name'
g.writeStringField("gender", Gender.MALE);
g.writeBooleanField("verified", false);
g.writeFieldName("userImage"); // no 'writeBinaryField' (yet?)
byte[] binaryData = ...;
g.writeBinary(binaryData);
g.writeEndObject();
g.close(); // important: will force flushing of output, close underlying output stream
Not horribly bad (esp. compared to amount of work needed for writing, say, equivalent XML content), but certainly more laborious than basic Object mapping.
On the other hand, you do have full control over each and every detail. And overhead is minimal: this is still a bit faster than using ObjectMapper; not a whole lot (perhaps 20-30% faster in common cases), but still. And perhaps most importantly, output is done in streaming manner: except for some buffering, all content will be written out right away. This means that memory usage is also minimal.
How about parsing, then? Code could look something like:
JsonFactory f = new JsonFactory();
JsonParser jp = f.createJsonParser(new File("user.json"));
User user = new User();
jp.nextToken(); // will return JsonToken.START_OBJECT (verify?)
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jp.getCurrentName();
jp.nextToken(); // move to value, or START_OBJECT/START_ARRAY
if ("name".equals(fieldname)) { // contains an object
Name name = new Name();
while (jp.nextToken() != JsonToken.END_OBJECT) {
String namefield = jp.getCurrentName();
jp.nextToken(); // move to value
if ("first".equals(namefield)) {
name.setFirst(jp.getText());
} else if ("last".equals(namefield)) {
name.setLast(jp.getText());
} else {
throw new IllegalStateException("Unrecognized field '"+fieldname+"'!");
}
}
user.setName(name);
} else if ("gender".equals(fieldname)) {
user.setGender(User.Gender.valueOf(jp.getText()));
} else if ("verified".equals(fieldname)) {
user.setVerified(jp.getCurrentToken() == JsonToken.VALUE_TRUE);
} else if ("userImage".equals(fieldname)) {
user.setUserImage(jp.getBinaryValue());
} else {
throw new IllegalStateException("Unrecognized field '"+fieldname+"'!");
}
}
jp.close(); // ensure resources get cleaned up timely and properly
which is quite a bit more than you'll use with data binding.
One final trick: it is also possible to use data binding and tree model directly from JsonParser and JsonGenerator. To do this, have a look at methods:
JsonParser.readValueAs()
JsonParser.readValueAsTree()
JsonGenerator.writeObject()
JsonGenerator.writeTree()
which do about what you might expect them to do.
The only (?) trick is that you MUST make sure you use org.codehaus.jackson.map.MappingJsonFactory for constructing "data-binding capable" parser and generator instances (instead of basic org.codehaus.jackson.JsonFactory).
Streaming API Example 2: arrays
Let's consider following POJO:
public class Foo {
public String foo;
}
and sample JSON stream of:
String json = [{\"foo\": \"bar\"},{\"foo\": \"biz\"}]";
while there are convenient ways to work on this with databinding (see ObjectReader.readValues() for details), you can easily use streaming to iterate over stream, bind individual elements as well:
JsonFactory f = new JsonFactory();
JsonParser jp = f.createJsonParser(json);
// advance stream to START_ARRAY first:
jp.nextToken();
// and then each time, advance to opening START_OBJECT
while (jp.nextToken() == JsonToken.START_OBJECT) {
Foo foobar = mapper.readValue(jp, Foo.class);
// process
// after binding, stream points to closing END_OBJECT
}
Next Steps
You may want to check out rest of JacksonDocumentation for more inspiration.
Jackson - Quickstart的更多相关文章
- jackson简单使用,对象转json,json转对象,json转list
添加jackson依赖: // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core compile g ...
- Jackson 通过自定义注解来控制json key的格式
Jackson 通过自定义注解来控制json key的格式 最近我这边有一个需求就是需要把Bean中的某一些特殊字段的值进行替换.而这个替换过程是需要依赖一个第三方的dubbo服务的.为了使得这个转换 ...
- [译]App Framework 2.1 (1)之 Quickstart
最近有移动App项目,选择了 Hybrid 的框架Cordova 和 App Framework 框架开发. 本来应该从配置循序渐进开始写的,但由于上班时间太忙,这段时间抽不出空来,只能根据心情和 ...
- 免安裝、免設定的 Hadoop 開發環境 - cloudera 的 QuickStart VM
cloudera 的 QuickStart VM,為一種免安裝.免設定 Linux 及 Hadoop,已幫你建好 CDH 5.x.Hadoop.Eclipse 的一個虛擬機環境.下載後解壓縮,可直接以 ...
- Jackson的简单用法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1简介 Jackson具有比较高的序列化和反序列化效率,据测试,无论是 ...
- jackson error 含义log
1. 反序列化失败,类型不匹配 Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserial ize ...
- jackson annotations注解详解
jackson中常用到的注解 猛击下面的连接地址 http://blog.csdn.net/sdyy321/article/details/40298081
- jackson官方快速入门文档
官方地址: http://jackson.codehaus.org/ http://wiki.fasterxml.com/JacksonInFiveMinutes http://wiki.faster ...
- Jackson将json字符串转换成泛型List
Jackson,我感觉是在Java与Json之间相互转换的最快速的框架,当然Google的Gson也很不错,但是参照网上有人的性能测试,看起来还是Jackson比较快一点 Jackson处理一般的Ja ...
随机推荐
- poj 3635 Full Tank? ( bfs+dp思想 )
Full Tank? Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5857 Accepted: 1920 Descri ...
- HTML5结构化标签
一 结构化标签 在之前的HTML页面中,大家基本上都是用了Div+CSS的布局方式.而搜索引擎去抓取页面的内容的时候,它只能猜测你的某个Div内的内容是文章内容容器,或者是导航模块的容器,或者是作者介 ...
- foxpro常用命令
Visual FoxPro原名FoxBase,最初是由美国Fox Software公司于1988年推出的数据库产品,在DOS上运行,与xBase系列兼容.FoxPro是FoxBase的加强版,最高版本 ...
- 目录启动CXF启动报告LinkageError异常以及Java的endorsed机制
本文纯属个人见解,是对前面学习的总结,如有描述不正确的地方还请高手指正~ Exception in thread "main" java.lang.LinkageError: JA ...
- Codeforces Beta Round #85 (Div. 1 Only) A. Petya and Inequiations 贪心
A. Petya and Inequiations Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest ...
- C++ Button右键弹出式菜单
Button右键弹出式菜单 关键点 用类来实现 的 实现过程 新建1个类 类名CButtonPopMenu 基类CButton 新建1个菜单资源 IDR_MENU1 // ButtonPopMenu ...
- HoloGraphLibrary
https://github.com/Androguide/HoloGraphLibrary HoloGraphLibrary-master.zip
- [Javascript] IO Functor
IO functor doesn't like Maybe(), Either() functors. Instead of get a value, it takes a function. API ...
- yii2 rbac 设计
tbl_auth_item 根据type存储认证项目...role.task.operation 游客... 不能操作任何模块 普通用户 ..biz_rule..需要登录 只有这里返回true,才能进 ...
- STL 案例分析
#include <iostream> using namespace std; #include "string" #include <vector> # ...