在Java平台(StAX, JAXB等)XML处理质量和多样化的激励下,Jackson为多功能的Java JSON处理包其目标为集快捷、正确、轻量和符合人体工程学与一体。
本文将给出Jackson的功能概览。
JSON的三种处理方式
Jackson提供了三种可选的JSON处理方法(一种方式及其两个变型):
用两行代码把它变成一个用户实例:
用户类大致如下(源自另一博客):
编组为JSON同样简单:
对于更复杂的数据绑定 (例如,反编排格式日期到 java.util.Date),Jackson提供注解来自定义编排和反编排的处理过程。
简单的数据绑定示例
如果你没有 (或不想创建)从 JSON到 Java 的相互转化类,简单数据绑定可能是更好的方法。它用相同方式实现完整的数据绑定,除非形式化绑定类型只指定为 Object.class (或 Map.class, List.class,即使需要更多的类型定义)。因此早期绑定JSON的用户数据可能如此实现:
userData 像一个的显式结构:
这显然是双向的: 如果利用诸如Map 的结构构建(或从 JSON绑定及修改),也可以如前法实现:
将如何工作呢? 只定义了Map.class,未定义一般的Key/valie类型,但ObjectMapper 却知晓与Map(及List、数组、wrapper类型 )之间如何相互转换,仅是如此即可。如果它可以正确地映射到您所提供的类型,它将被映射。
Jackson将使用简单数据绑定的具体Java 类型包括:
泛型的数据绑定
除绑定到POJO和简单类型外,还有一个额外的变型:绑定到泛型(类型)容器。此时,由于所谓的类型擦除(Java采用向后兼容的方式实现泛型),需要进行特殊处理,以防止使用类似 Collection<String>.class(不被编译)。
所以,热想绑定数据岛Map<String,User>,方式如下:
其中TypeReference只需传入泛型类型即可(此时需要匿名内部类):重要部分为<Map<String,User>>,定义要绑定的数据类型。
若不如此(仅定义Map.class),其调用等价于绑定到 Map<?,?>(亦即 “untyped” Map),如前所述。
更新:1.3版的Jackson允许利用TypeFactory实现构造类型。
树模式示例
另一种从JSON获取对象方式是构造“树”,类似于XML的DOM树。Jackson构造树的方法利用JsonNode基类,其中包含公开的通常所需的读取访问方法,实际所用的节点类型为其子类;但子类型仅在修改树时需要。
JSON树可通过流式API或ObjectMapper方式读、写。
利用 ObjectMapper,方法如下:
或你想马上构造一棵树,方法如下:
(注意: Jackson 1.2可直接使用ObjectMapper:通过ObjectMapper.createObjectNode()创建userOb -- 上例工作于Jackson 1.0 和 1.1)。
流式 API 示例
最后,还有第三种方式: 涡轮增压、 高性能的方法称为流 API (或增量模式,因为内容是增量读取和写入的)。
只是为了好玩,让我们实现使用"原生"Stream API 的写入功能 (相当于前面示例): WriteJSON.java
非常不错 (尤其是相对写入所需的工作量,亦即等效的 XML 内容),但肯定比基本对象映射更辛苦。
另一方面,必须完全控制每一个细节。开销很小: 这仍然快于使用 ObjectMapper;并非快很多 ,但还是要快些(一般快或许 20-30%)。也许最重要的是,以流方式输出: 除一些缓冲外,所有内容都将马上输出。这意味着该方式内存使用量也是最小的。
然后如何解析呢?代码可能看起来类似:
这是不是您将更多使用的数据绑定方法。
最后提醒的一个窍门: 可可能通过JsonParser 和 JsonGeneratorit 直接实现数据绑定和树模式。请参阅如下方法:
将实现你期望的结果。
切记,确保所用的 org.codehaus.jackson.map.MappingJsonFactory是"适用数据绑定“的解析器和生成器实例(而非基本的org.codehaus.jackson.JsonFactory)。
原文:http://simpleframework.net/news/view?newsId=15d30bbad7954851ab14aaae4f7d9f5b
- 流式 API:(也称为"增量分析/生成") 读取和写入 JSON 内容作为离散事件。
- org.codehaus.jackson.JsonParser 读, org.codehaus.jackson.JsonGenerator 写。
- StAX API 的激励。
- 树模型 :提供一个 JSON 文档可变内存树的表示形式。
- org.codehaus.jackson.map.ObjectMapper 生成树 ;树组成 JsonNode 节点集。
- 树模型类似于 XML DOM。
- 数据绑定: JSON和POJO相互转换,基于属性访问器规约或注解。
- 有两种变体: 简单 和 完整 的数据绑定:
- 简单数据绑定: 是指从Java Map、List、String、Numbers、Boolean和空值进行转换
- 完整数据绑定 :是指从任何 Java bean 类型 (及上文所述的"简单"类型) 进行转换
- org.codehaus.jackson.map.ObjectMapper 对两个变种,进行编组(marshalling )处理 (写入 JSON) 和反编组(unmarshalling ,读 JSON)。
- JAXB激励下的基于注释的 (代码优先)变种。
- 流 API: 性能最佳的方式 (最低开销、 速度最快的读/写; 其它二者基于它实现)。
- 数据绑定 :使用最方便的方式。
- 树模型: 最灵活的方式。
1
2
3
4
5
6 |
{ "name" : { "first" : "Joe" , "last" : "Sixpack" }, "gender" : "MALE" , "verified" : false , "userImage" : "Rm9vYmFyIQ==" } |
1
2 |
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally User user = mapper.readValue( new File( "user.json" ), User. class ); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 |
public class User { 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( final Name n) { _name = n; } public void setVerified( final boolean b) { _isVerified = b; } public void setGender( final Gender g) { _gender = g; } public void setUserImage( final byte [] b) { _userImage = b; } public static class Name { private String _first; private String _last; public String getFirst() { return _first; } public String getLast() { return _last; } public void setFirst( final String s) { _first = s; } public void setLast( final String s) { _last = s; } } public enum Gender { MALE, FEMALE; } } |
1 |
mapper.writeValue( new File( "user-modified.json" ), user); |
1 |
Map<String,Object> userData = mapper.readValue( new File( "user.json" ), Map. class ); |
1
2
3
4
5
6
7
8 |
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==" ); |
1 |
Map<String,Object> userData = mapper.readValue( new File( "user.json" ), Map. class ); |
JSON Type | Java Type |
object | LinkedHashMap<String,Object> |
array | ArrayList<Object> |
string | String |
number(no fraction) | Integer, Long or BigInteger (smallest applicable) |
number (fraction) | BigDecimal |
true|false | boolean |
null | null |
1 |
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() { }); |
1
2
3
4
5
6
7
8
9
10
11 |
ObjectMapper m = new ObjectMapper(); // can either use mapper.readTree(JsonParser), or bind to JsonNode JsonNode rootNode = m.readValue( new File( "user.json" ), JsonNode. class ); // 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); |
1
2
3
4
5
6
7
8
9 |
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); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
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(); // 重要:强制写入输出,并关闭输出流! |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 |
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(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 |
1
2
3
4 |
JsonParser.readValueAs() JsonParser.readValueAsTree() JsonGenerator.writeObject() JsonGenerator.writeTree() |