标签:des style http color java 使用 os io
Inspired by the quality and variety of XML tooling available for the Java platform (StAX, JAXB, etc.), the Jackson is a multi-purpose Java library for processing JSON. Jackson aims to be the best possible combination of fast, correct, lightweight, and ergonomic for developers.
本文简单描述 Jackson 的功能。
Jackson 为处理 JSON 提供三种可选的方法(其中一个,有两个变体):
从使用的角度,这三种方式:
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.
org.codehaus.jackson.map.ObjectMapper 用于将 JSON 数据映射成普通的 Java 对象(plain old Java objects,POJOs)。例如,对于给定的 JSON 数据:
{
"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; }
}
把 User 对象再转换成 JSON,并保存名为 user-modified.json 文件,如下所示:
mapper.writeValue(new File("user-modified.json"), user);
对于发烧友数据绑定(例如,把格式化的日期编排成 java.util.Date),Jackson 提供注解自定义编排(marshal,对数据存储结构的重新编排转换,而不是数据结构)的处理。
(也称为“非类型”,或有时称为“简单”数据绑定)
在一些情况,我们没有明确的 Java 类(也不想这么做)去绑定 JSON,那么“非类型的数据绑定”是最好的方法。它的使用与完全数据绑定一样,只是简单地规定把 Object.class(或是 Map.class,List.class,String[].class 等)作为绑定类型。因此,User 的 JSON 绑定如下所示:
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:如果你构造一个 Map(或从 JSON 构造,并进行修改),那么你可以跟之前一样写成 JSON 文件:
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.
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 和“简单”类型外,还可以绑定泛型。
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).
因此,如果你想绑定数据到 Map<String,User>,你需要使用:
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.
Yet another way to get Objects out of JSON is to build a tree. 这类似 XML 的 DOM 树。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)
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:
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).
考虑下面的 POJO:
public class Foo {
public String foo;
}
以及 JSON 流:
String json = [{\"foo\": \"bar\"},{\"foo\": \"biz\"}]";
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
}
标签:des style http color java 使用 os io
原文地址:http://www.cnblogs.com/liuning8023/p/3898227.html