【浅度渣文】Jackson之jackson-databind

栏目: C++ · 发布时间: 5年前

内容简介:前几篇介绍Jackson的文章(Jackson介绍,这篇文章介绍使用高级的API,让你看到Jackson也可以这么的简单,容易。

原文链接:http://www.dubby.cn/detail.html?id=9070

前几篇介绍Jackson的文章(Jackson介绍, Jackson之jackson-core ),虽然很好,但是我相信你并愿意在项目中使用,因为使用起来很复杂,也许这也是很多人愿意使用Fastjson的原因吧。为什么会感觉这么复杂呢,因为jackson-core提供的是很低级的API,我们可以充分的了解细节,但是代价就是操作起来更复杂。

这篇文章介绍使用高级的API,让你看到Jackson也可以这么的简单,容易。

Maven依赖

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.2</version>
</dependency>
复制代码

因为jackson-databind依赖core和annotations,所以在这里需要依赖这三个jar。

POJO和JSON之间的转化

给出一个足够简单的POJO:

public class MyValue {
  public String name;
  public int age;
}
复制代码

注意:如果使用getters/setters的话,可以用private/protected修饰属性,这里直接用public修饰了,就不需要getters/setters了。

使用 databind ,我们需要一个最基础的对象 com.fasterxml.jackson.databind.ObjectMapper ,这里我们构造一个:

ObjectMapper mapper = new ObjectMapper(); 
复制代码

注意:这个mapper是可以复用的,就好比HttpClient一样。

简单的把JSON反序列化成Object的用法如下:

MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
// or:
value = mapper.readValue(new URL("http://www.dubby.cn/api/entry.json"), MyValue.class);
// or:
value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
复制代码

简单的把Object序列化成JSON的用法如下:

mapper.writeValue(new File("result.json"), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);
复制代码

其实到这一步,对于很多读者来说已经足够了。因为大部分时候我们要的就是这些。但是不妨继续看下去,还有一些你可能会用到的。

集合、树

如果你使用的不是简单的POJO,而是 List , Map :

Map<String, Integer> scoreByName = mapper.readValue(jsonSource, Map.class);
List<String> names = mapper.readValue(jsonSource, List.class);

mapper.writeValue(new File("names.json"), names);
复制代码

如果你反序列化的更复杂,你可以指定类型:

Map<String, ResultValue> results = mapper.readValue(jsonSource, new TypeReference<Map<String, ResultValue>>() { } );
复制代码

思考:为什么需要指定类型?(类型擦除)

注意:序列化的时候不需要指定,只有反序列化的时候需要。

虽然看起来处理的很方便,但是某些时候会有一些很麻烦的情况,这时候可以考虑使用 树模型 :

//如果结果可能是Object或者是Array,那可以使用JsonNode;
//如果你知道是Object,你可以直接强转成ObjectNode;如果你知道是Array,你可以直接强转成ArrayNode;
ObjectNode root = (ObjectNode) mapper.readTree("stuff.json");
String name = root.get("name").asText();
int age = root.get("age").asInt();

// 还可以修改这个树,然后再输出成json字符串
root.with("other").put("type", "student");
String json = mapper.writeValueAsString(root);

// with above, we end up with something like as 'json' String:
// {
//   "name" : "Bob", "age" : 13,
//   "other" : {
//      "type" : "student"
//   }
// }
复制代码

上面的例子中的JSON如下:

{
  "name" : "Bob", "age" : 13,
  "other" : {
     "type" : "student"
  }
}
复制代码

如果 json 类型太过动态,不适合反序列化成对象的时候,树模型比数据绑定更合适。

流式解析器、生成器

看完上面的介绍,我想你应该相当满意 ObjectMapper 的能力了,但是如果你希望控制底层的一些细节,或者对性能有更高的要求,你可以通过 ObjectMapper 来设置。建议你先看看 Jackson之jackson-core :

JsonFactory f = mapper.getFactory();
// 1、输入JSON字符串
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonGenerator g = f.createGenerator(outputStream);
// 输出JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();
// 2、把JSON字符串反序列化
JsonParser p = f.createParser(outputStream.toString());
JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
    // handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
    // similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
p.close();
复制代码

你也可以直接构造 JsonFactory ,然后作为构造参数传给 ObjectMapper

配置

有两个方面的配置, 特性注解

特性配置

给出一个简单的使用特性配置的例子,先给出序列化配置:

// 设置序列化成漂亮的JSON,而不是压缩的字符串
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// 如果你要序列化的对象没有字段(很搞笑吧),会抛异常,可以设置这个来避免异常,直接序列化成`{}`
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 默认Date会序列化成时间戳,可以设置这个来序列化成`date":"2017-12-09T12:50:13.000+0000`这个样子
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
复制代码

反序列化配置的例子:

// 默认,如果反序列化时,JSON字符串里有字段,而POJO中没有定义,会抛异常,可以设置这个来忽略未定义的字段
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 默认如果是字符串(""),反序列化会失败,可以开启这个设置,字符串("")会被反序列化成(null)
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
复制代码

除此之外,还可以针对序列化和反序列化的底层细节指定一些配置,先给出parsing的配置:

// 默认如果JSON中有C/C++风格的注释,在反序列化的时候会报错,可以指定这个配置来忽略C/C++风格的注释
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
//默认JSON字符串如果字段名没有用双引号包裹,回报错,可以设置这个来支持这种非正规的JSON(JS支持这种非正规的JSON)
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 默认如果JSON中是用的单引号包裹字段和值,反序列化时会报错,可以设置这个来兼容单引号这种非正规的JSON
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
复制代码

再给出generation的配置:

// 把非ASCII转义成ASCII值,如(杨正)会被转义成(\u6768\u6B63)
mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
复制代码

注解配置

改变字段名

注解 @JsonProperty :

public class MyBean {
   private String _name;

   // 默认是`theName`,现在改为`name`
   @JsonProperty("name")
   public String getTheName() { return _name; }

   // 只需要修饰getter或者setter其中的一个就可以了,这里可以省略不写
   public void setTheName(String n) { _name = n; }
}
复制代码

忽略字段

@JsonIgnore 可以忽略单个字段, @JsonIgnoreProperties 可以加在类定义上:

// 序列化和反序列化时,直接忽略JSON中的foo和bar字段
@JsonIgnoreProperties({ "foo", "bar" })
public class MyBean
{
   // 序列化和反序列化时,直接忽略JSON中的internal字段
   @JsonIgnore
   public String internal;

   // 正常字段
   public String external;

   @JsonIgnore
   public void setCode(int c) { _code = c; }

   // 虽然这里没有修饰,但是setter被修饰了,所以也会被忽略
   public int getCode() { return _code; }
}
复制代码

从上面我们可以看出,注解在字段名、setter和getter上都是一样的,修饰任何一个都会直接忽略这个字段,但是我们可以值忽略反序列化,而不忽略序列化,或者反之:

public class ReadButDontWriteProps {
   private String _name;
   @JsonProperty public void setName(String n) { _name = n; }
   @JsonIgnore public String getName() { return _name; }
}
复制代码

这里使用 @JsonProperty 保证,虽然序列化是name会被忽略,但是从JSON中反序列化时,可以正常接收这个字段。

自定义构造方法

和其他数据绑定 工具 不一样,Jackson不会强制要求你的POJO必须有个默认构造方法(无参构造方法)。你可以指定一个构造方法来接收反序列化的字段值:

public class CtorBean
{
  public final String name;
  public final int age;

  @JsonCreator
  private CtorBean(@JsonProperty("name") String name,
    @JsonProperty("age") int age)
  {
      this.name = name;
      this.age = age;
  }
}
复制代码

构造方法可以是public,private或者任何其他修饰符修饰

对于一些不可改变的对象,这个会很有用,除了构造方法, @JsonCreator 这个注解还可以定义一个工厂方法:

public class FactoryBean
{
    // fields etc omitted for brewity

    @JsonCreator
    public static FactoryBean create(@JsonProperty("name") String name) {
      // construct and return an instance
    }
}
复制代码

注意:构造方法( @JsonCreator@JsonProperty )和setter不互斥,你可以混合使用。

填充、转换

Jackson还有一个很有意思的功能,虽然没有广泛的被人所知道。那就是POJO和POJO之间的转换。概念性的可以理解成POJO1->JSON->POJO2,但是实际上会省略中间这一步,不会真正的生成JSON,而会用其他更高效的实现:

ResultType result = mapper.convertValue(sourceObject, ResultType.class);
复制代码

还有其他用法:

// List<Integer> -> int[]
List<Integer> sourceList = ...;
int[] ints = mapper.convertValue(sourceList, int[].class);
// POJO -> Map
Map<String,Object> propertyMap = mapper.convertValue(pojoValue, Map.class);
// Map -> POJO
PojoType pojo = mapper.convertValue(propertyMap, PojoType.class);
// decode Base64! (default byte[] representation is base64-encoded String)
复制代码

甚至还可以解码base64码:

//解码
String base64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz";
byte[] binary = mapper.convertValue(base64, byte[].class);
System.out.println(new String(binary));
//编码
String str = "Man is distinguished, not only by his reason, but by this";
String base = mapper.convertValue(str.getBytes(), String.class);
System.out.println(base);
复制代码

所以,Jackson甚至强大到可以代替Apache Commons组件。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning jQuery

Learning jQuery

Jonathan Chaffer、Karl Swedberg / Packt Publishing / 2007-7-7 / GBP 24.99

jQuery is a powerful JavaScript library that can enhance your websites regardless of your background. In this book, creators of the popular jQuery learning resource, learningquery.com, share the......一起来看看 《Learning jQuery》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试