Jackson 概述_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > Jackson 概述

Jackson 概述

 2014/8/10 11:05:27  IGod接口  程序员俱乐部  我要评论(0)
  • 摘要:原文地址本文内容JSON的三种方式示例完全数据绑定(POJO)示例“Raw”数据绑定示例用泛型数据绑定树模型(TreeModel)示例流(Streaming)API示例流(Streaming)API示例2:数组参考资料演示代码最近写AndroidAPP,需要序列化和反序列化。但是遇到一些问题,因为我是搞.NET的,怎么感觉比Newtonsoft要麻烦呢,于是就顺便研究了一下Jackson。受到质量和各种各样的在Java平台(StAX,JAXB,等等)上XML工具的启发
  • 标签:

原文地址

本文内容

  • JSON 的三种方式
  • 示例
    • 完全数据绑定(POJO)示例
    • “Raw”数据绑定示例
    • 泛型数据绑定
    • 树模型(Tree Model)示例
    • 流(Streaming)API 示例
    • 流(Streaming)API 示例 2: 数组
  • 参考资料
  • 演示代码

最近写 Android APP,需要序列化和反序列化。但是遇到一些问题,因为我是搞 .NET 的,怎么感觉比 Newtonsoft 要麻烦呢,于是就顺便研究了一下 Jackson。

受到质量和各种各样的在 Java 平台(StAX, JAXB, 等等)上 XML 工具的启发,Jackson 是一个用于处理 JSON 多用途的 Java 库。Jackson 的目标是对 Java 开发者来说最大可能的结合快速的、准确的、轻量级的和符合人体工程学。

本文使用 jackson-all-1.9.11 简单描述 Jackson 的功能。

下载 Demo

JSON 的三种方式


Jackson 为处理 JSON 提供三种可选的方法(其中的一个有两个变体):

  • Streaming API(又称“增量解析/生成”)把读写 JSON 内容作为离散事件。
    • org.codehaus.jackson.JsonParser 读,用 org.codehaus.jackson.JsonGenerator 写。
    • 基于 StAX API。
  • 树模型(Tree Model)提供了一个表示 JSON 文档的可变内存树。
    • org.codehaus.jackson.map.ObjectMapper 可以生成树;树由 JsonNode 节点组成。
    • 树模型类似 XML DOM。
  • 数据绑定(Data Binding)把 JSON 和 基于属性访问器(property accessor)或注释的 POJO 之间进行转换。
    • 有两个变体:简单数据绑定和完全数据绑定
      • 简单数据绑定,意思是从或到 Java Map、List、String、Number、Boolean 和 null 的转换
      • 完整数据绑定,意思是从或到任何 Java bean 类型(以及上面提到的“简单”类型)的转换
    • org.codehaus.jackson.map.ObjectMapper 完成重排(marshalling/unmarshalling,对数据存储结构的重新编排转换,而不是数据结构),包括把对象写成 JSON,或读取 JSON 转换成对象
    • 基于基于注解(代码优先)变种的 JAXB。

从使用的角度,这三种方式:

  • 流 API,具有最好的性能(最低的开销,最快的读写;其他两个都是基于它的)
  • 数据绑定,通常最方便
  • 树模型,最灵活

对于这些特性,考虑倒着介绍,先从平时最自然最方便的方法开始:Jackson 数据绑定 API。

 

示例


完全数据绑定(POJO)示例

org.codehaus.jackson.map.ObjectMapper 用于将 JSON 数据映射成普通的 Java 对象(plain old Java objects,POJOs)。例如,对于给定的 JSON 数据:

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

用两行 Java 代码就可以把它转换成一个 User 实例:

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

User 类的定义如下所示:

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;
    }
 
    public String toString() {
        return "name=" + this._name._last + " " + this._name._first
                + ",gender=" + this._gender;
    }
}

User 对象再转换成 JSON,并保存名为 user-modified.json 文件,如下所示:

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

对于某些数据绑定(例如,把格式化的日期编排成 java.util.Date),Jackson 提供注解自定义重排的处理。

“Raw”数据绑定示例

(也称为“非类型”,或有时称为“简单”数据绑定)

在一些情况,我们没有明确的 Java 类(也不想这么做)去绑定 JSON,那么“非类型的数据绑定”是最好的方法。它的使用与完全数据绑定一样,只是简单地规定把 Object.class(或是 Map.classList.classString[].class 等)作为绑定类型。因此,User 的 JSON 绑定如下所示:

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

userData 可以如下面代码显示构造:

        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==");

如果你构造一个 Map,如上所示,或从 JSON 构造,并可能进行修改,那么你可以跟之前一样写成 JSON 文件:

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

Jackson 用于简单数据绑定的具体 Java 类型:

JSON Type Java Type object LinkedHashMap<String,Object> array ArrayList string String number(非小数) Integer, Long 或 BigInteger (smallest applicable) number(小数) Double (configurable to use BigDecimal) true|false Boolean null null

用泛型数据绑定

除了绑定 POJOs 和“简单”类型外,还可以绑定泛型。

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).

因此,如果你想绑定数据到 Map<String,User>你需要使用:

        Map<String, User> userData = mapper.readValue(new File(
                "user-modified-generic.json"),
                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)示例

另一种从 JSON 获得对象的方式是建立一棵树。这类似 XML 的 DOM 树。Jackson 生成树的方式是使用基本的 JsonNode 基类,它公开了通常需要的读取访问。实际使用的节点类型是其子类;但子类只是在需要修改树时使用。

可以用 Streaming API (如下面小节)或是 ObjectMapper 来读/写树。

若用 ObjectMapper,则可以像下面的代码那样:

        ObjectMapper mapper = new ObjectMapper();
        // can either use mapper.readTree(source), or mapper.readValue(source,
        // JsonNode.class);
        JsonNode rootNode = mapper.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:
        mapper.writeValue(new File("user-modified-tree.json"), rootNode);

或者,你想用 User 示例从头构造一个树,你可以这样:

        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);

注意:对于 Jackson 1.2,你可以直接使用 ObjectMapper,通过使用 ObjectMapper.createObjectNode() 来创建 userOb——以上代码是针对 Jackson 1.0 和 1.1)。

Streaming API 示例

最后,第三种方式:高性能的流 API(又称“增量模式”,因为可以增量地读写内容)。

        JsonFactory f = new JsonFactory();
        JsonGenerator g = f.createJsonGenerator(new File("user-stream.json"),
                JsonEncoding.UTF8);
        g.writeStartObject();
        g.writeObjectFieldStart("name");
        g.writeStringField("first", "Joe");
        g.writeStringField("last", "Sixpack");
        g.writeEndObject(); // for field 'name'
        g.writeStringField("gender", Gender.MALE.toString());
        g.writeBooleanField("verified", false);
        g.writeFieldName("userImage"); // no 'writeBinaryField' (yet?)
        byte[] binaryData = "Rm9vYmFyIQ==".getBytes();
        g.writeBinary(binaryData);
        g.writeEndObject();
        // important: will force flushing of output, close underlying output
        // stream
        g.close();

也不是特别糟糕(特别是,与需要写的工作量相比,可以说,相当于 XML 内容),但肯定比基本对象映射费劲点。

在另一方面,你也可以完全控制每个细节。开销最小:这比使用 ObjectMapper 要快一点;通常情况快 20-30%。但或许最重要的是,已流的方式输出:除了一些缓冲,所有内容会将立刻写出来。这意味着,内存的使用也是最小的。

那么如何解析?如下代码所示:

        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 + "'!");
            }
        }
        // ensure resources get cleaned up timely and properly
        jp.close();

最后,可能直接利用 JsonParser 和 JsonGenerator 进行数据绑定和树模型。你可以看下下面方法:

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

你必须使用 org.codehaus.jackson.map.MappingJsonFactory 来构造解析器和生成器,而不是 org.codehaus.jackson.JsonFactory

Streaming API 示例 2:数组

考虑下面的 POJO:

    public class Foo {
        public String foo;
    }

以及 JSON 流:

        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
          }

 

参考资料


  • Jackson – JsonMappingException (No serializer found for class)
  • Jackson JSON Tutorial
  • Jackson 官网

 

演示代码


  • Windows 7 64 位

 

下载 Demo

  • 相关文章
发表评论
用户名: 匿名