Java JSON(二)5分钟学会Jackson

Java JSON(二)5分钟学会Jackson

http://simpleframework.net/blog/v/20118.html


在Java平台(StAX, JAXB等)XML处理质量和多样化的激励下,Jackson为多功能的Java JSON处理包其目标为集快捷、正确、轻量和符合人体工程学与一体。

本文将给出Jackson的功能概览。

JSON的三种处理方式 
Jackson提供了三种可选的JSON处理方法(一种方式及其两个变型):

  • 流式 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激励下的基于注释的 (代码优先)变种。

从使用的角度来看,总结这些3 种方法的用法如下:

  • 流 API: 性能最佳的方式 (最低开销、 速度最快的读/写; 其它二者基于它实现)。

  • 数据绑定 :使用最方便的方式。

  • 树模型: 最灵活的方式。

鉴于这些特性,让我们考虑以相反的顺序,以Java开发人员最自然和方便的方法开始使用: 杰Jackson数据绑定 API。

Jackson的 org.codehaus.jackson.map.ObjectMapper "只是"将JSON 数据映射为POJO 对象 。例如,给定 JSON 数据:

{
  "name" : { "first" : "Joe", "last" : "Sixpack" },
  "gender" : "MALE",
  "verified" : false,
  "userImage" : "Rm9vYmFyIQ=="
}

用两行代码把它变成一个用户实例:

1 ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
2 User user = mapper.readValue(new File("user.json"), User.class);

用户类大致如下(源自另一博客):

1 public class User {
2 public enum Gender { MALE, FEMALE };
3
4 public static class Name {
5 private String _first, _last;
6
7 public String getFirst() { return _first; }
8 public String getLast() { return _last; }
9
10 public void setFirst(String s) { _first = s; }
11 public void setLast(String s) { _last = s; }
12 }
13
14 private Gender _gender;
15 private Name _name;
16 private boolean _isVerified;
17 private byte[] _userImage;
18
19 public Name getName() { return _name; }
20 public boolean isVerified() { return _isVerified; }
21 public Gender getGender() { return _gender; }
22 public byte[] getUserImage() { return _userImage; }
23
24 public void setName(Name n) { _name = n; }
25 public void setVerified(boolean b) { _isVerified = b; }
26 public void setGender(Gender g) { _gender = g; }
27 public void setUserImage(byte[] b) { _userImage = b; }
28 }

编组为JSON同样简单:

mapper.writeValue(new File("user-modified.json"), user);

 对于更复杂的数据绑定 (例如,反编排格式日期到 java.util.Date),Jackson提供注解来自定义编排和反编排的处理过程。

简单的数据绑定示例
如果你没有 (或不想创建)从 JSON到 Java 的相互转化类,简单数据绑定可能是更好的方法。它用相同方式实现完整的数据绑定,除非形式化绑定类型只指定为 Object.class (或 Map.class, List.class,即使需要更多的类型定义)。因此早期绑定JSON的用户数据可能如此实现:

Map<String,Object> userData = mapper.readValue(new File("user.json"), Map.class);

 userData 像一个的显式结构:

   1 Map<String,Object> userData = new HashMap<String,Object>();    2 Map<String,String> nameStruct = new HashMap<String,String>();    3 nameStruct.put("first", "Joe");    4 nameStruct.put("last", "Sixpack");    5 userData.put("name", nameStruct);    6 userData.put("gender", "MALE");    7 userData.put("verified", Boolean.FALSE);    8 userData.put("userImage", "Rm9vYmFyIQ=="); 
这显然是双向的: 如果利用诸如Map 的结构构建(或从 JSON绑定及修改),也可以如前法实现:
Map<String,Object> userData = mapper.readValue(new File("user.json"), Map.class);
将如何工作呢? 只定义了Map.class,未定义一般的Key/valie类型,但ObjectMapper 却知晓与Map(及List、数组、wrapper类型 )之间如何相互转换,仅是如此即可。如果它可以正确地映射到您所提供的类型,它将被映射。
 
Jackson将使用简单数据绑定的具体Java 类型包括:
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

泛型的数据绑定

除绑定到POJO和简单类型外,还有一个额外的变型:绑定到泛型(类型)容器。此时,由于所谓的类型擦除(Java采用向后兼容的方式实现泛型),需要进行特殊处理,以防止使用类似 Collection<String>.class(不被编译)。

所以,热想绑定数据岛Map<String,User>,方式如下:

Map<String,User> result = mapper.readValue(src, new TypeReference<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,方法如下:

  1 ObjectMapper m = new ObjectMapper();
   2 // can either use mapper.readTree(JsonParser), or bind to JsonNode
   3 JsonNode rootNode = m.readValue(new File("user.json"), JsonNode.class);
   4 // ensure that "last name" isn't "Xmler"; if is, change to "Jsoner"
   5 JsonNode nameNode = rootNode.path("name");
   6 String lastName = nameNode.path("last").getTextValue().
   7 if ("xmler".equalsIgnoreCase(lastName)) {
   8   ((ObjectNode)nameNode).put("last", "Jsoner");
   9 }
  10 // and write it out:
  11 m.writeValue(new File("user-modified.json"), rootNode);

 或你想马上构造一棵树,方法如下:

  1 TreeMapper treeMapper = new TreeMapper();
   2 ObjectNode userOb = treeMapper.objectNode();
   3 Object nameOb = userRoot.putObject("name");
   4 nameOb.put("first", "Joe");
   5 nameOb.put("last", "Sixpack");
   6 userOb.put("gender", User.Gender.MALE.toString());
   7 userOb.put("verified", false);
   8 byte[] imageData = getImageData(); // or wherever it comes from
   9 userOb.put("userImage", imageData);

 (注意: Jackson 1.2可直接使用ObjectMapper:通过ObjectMapper.createObjectNode()创建userOb -- 上例工作于Jackson 1.0 和 1.1)。

流式 API 示例
 
最后,还有第三种方式: 涡轮增压、 高性能的方法称为流 API (或增量模式,因为内容是增量读取和写入的)。

只是为了好玩,让我们实现使用"原生"Stream  API 的写入功能 (相当于前面示例): WriteJSON.java

   1 JsonFactory f = new JsonFactory();
   2 JsonGenerator g = f.createJsonGenerator(new File("user.json"));
   3
   4 g.writeStartObject();
   5 g.writeObjectFieldStart("name");
   6 g.writeStringField("first", "Joe");
   7 g.writeStringField("last", "Sixpack");
   8 g.writeEndObject(); // for field 'name'
   9 g.writeStringField("gender", Gender.MALE);
  10 g.writeBooleanField("verified", false);
  11 g.writeFieldName("userImage"); // no 'writeBinaryField' (yet?)
  12 byte[] binaryData = ...;
  13 g.writeBinary(binaryData);
  14 g.writeEndObject();
  15 g.close(); // 重要:强制写入输出,并关闭输出流!

 非常不错 (尤其是相对写入所需的工作量,亦即等效的 XML 内容),但肯定比基本对象映射更辛苦。

另一方面,必须完全控制每一个细节。开销很小: 这仍然快于使用 ObjectMapper;并非快很多 ,但还是要快些(一般快或许 20-30%)。也许最重要的是,以流方式输出: 除一些缓冲外,所有内容都将马上输出。这意味着该方式内存使用量也是最小的。

然后如何解析呢?代码可能看起来类似:

   1 JsonFactory f = new JsonFactory();
   2 JsonParser jp = f.createJsonParser(new File("user.json"));
   3 User user = new User();
   4 jp.nextToken(); // will return JsonToken.START_OBJECT (verify?)
   5 while (jp.nextToken() != JsonToken.END_OBJECT) {
   6   String fieldname = jp.getCurrentName();
   7   jp.nextToken(); // move to value, or START_OBJECT/START_ARRAY
   8   if ("name".equals(fieldname)) { // contains an object
   9     Name name = new Name();
  10     while (jp.nextToken() != JsonToken.END_OBJECT) {
  11       String namefield = jp.getCurrentName();
  12       jp.nextToken(); // move to value
  13       if ("first".equals(namefield)) {
  14         name.setFirst(jp.getText());
  15       } else if ("last".equals(namefield)) {
  16         name.setLast(jp.getText());
  17       } else {
  18         throw new IllegalStateException("Unrecognized field '"+fieldname+"'!");
  19       }
  20     }
  21     user.setName(name);
  22   } else if ("gender".equals(fieldname)) {
  23     user.setGender(Gender.valueOf(jp.getText()));
  24   } else if ("verified".equals(fieldname)) {
  25     user.setVerified(jp.getCurrentToken() == JsonToken.VALUE_TRUE);
  26   } else if ("userImage".equals(fieldname)) {
  27     user.setUserImage(jp.getBinaryValue());
  28   } else {
  29     throw new IllegalStateException("Unrecognized field '"+fieldname+"'!");
  30   }
  31 }
  32 jp.close(); // ensure resources get cleaned up timely and properly

 这是不是您将更多使用的数据绑定方法。

最后提醒的一个窍门: 可可能通过JsonParser 和 JsonGeneratorit 直接实现数据绑定和树模式。请参阅如下方法:

  • JsonParser.readValueAs()
  • JsonParser.readValueAsTree()
  • JsonGenerator.writeObject()
  • JsonGenerator.writeTree()

将实现你期望的结果。
切记,确保所用的 org.codehaus.jackson.map.MappingJsonFactory是"适用数据绑定“的解析器和生成器实例(而非基本的org.codehaus.jackson.JsonFactory)。

你可能感兴趣的:(Java JSON(二)5分钟学会Jackson)