Spring默认使用的JSON工具--Jackson

 


一、Jackson介绍

我们常用的json转换工具包括fastJson、Gson、Jackson等。其中Gson是Google所维护,功能全。fastJson特点是快,但是爆出几次的重大bug让人很难再去选择它。fastjson这么快老外为啥还是热衷 jackson? JackSon是Spring的御用工具,和Spring无缝集成,提供灵活的定制化开发的注解。如果使用Spring框架进行开发,建议使用JackSon。

Jackson包含两个不同的解析器:

  1. Jackson ObjectMapper,将JSON转化为Java对象,或者转换为Jackson特定的树结构
  2. Jackson JsonParser,JSON流解析器,每次只解析一个JSON标记(暂不做讲解)

Jackson还包含两个不同的生成器:

  1. Jackson ObjectMapper,可以从Java对象生成JSON,或者是从Jackson的树结构生成JSON
  2. Jackson Generator,每次生成一个JSON标记(暂不做讲解)

 


二、 ObjectMapper

1.ObjectMapper解析JSON的原理

默认情况下,Jackson通过Java bean的get,set方法,通过去掉get,set再把首字母小写得到的名字去和JSON的属性进行匹配。例如对于getBrand()和setBrand()经过处理得到brand,就会匹配到JSON的brand属性,从而把JSON brand属性的值赋给bean的brand字段。通过一系列这样的处理,就将JSON转换成了Java bean。如果需要以不同的方式来匹配,那么就得使用定制的serializer和deserializer,或者使用Jackson提供的众多的注解。

2.通过ObjectMapper序列化和反序列化

//car类定义
public class Car {
   private String brand = null;
   private Integer doors = 0;
   // get set......
}

反序列化代码: 

String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
// 从字符串创建(最常用的方式)
Car car = objectMapper.readValue(carJson, Car.class);
System.out.println(objectMapper.writeValueAsString(car));

String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
// 解析为数组
Car[] cars = objectMapper.readValue(jsonArray, Car[].class);
System.out.println(objectMapper.writeValueAsString(cars));

// 解析为list
List<Car> carList = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>() {});
System.out.println(objectMapper.writeValueAsString(carList));

String jsonObjectStr = "{\"brand\":\"ford\", \"doors\":5}";
// 解析为Map
Map<String, Object> map = objectMapper.readValue(jsonObjectStr,new TypeReference<Map<String, Object>>() {});
System.out.println(objectMapper.writeValueAsString(map));

// 从Reader创建
Reader reader = new StringReader(carJson);
car = objectMapper.readValue(reader, Car.class);
System.out.println(objectMapper.writeValueAsString(car));

File file = ResourceUtils.getFile("classpath:car.json");
// 从文件创建
car = objectMapper.readValue(file, Car.class);
System.out.println(objectMapper.writeValueAsString(car));

URL url = ResourceUtils.getFile("classpath:car.json").toURI().toURL();
// 从URL创建
car = objectMapper.readValue(url, Car.class);
System.out.println(objectMapper.writeValueAsString(car));

InputStream input = new FileInputStream(file);
// 从InputStream创建
car = objectMapper.readValue(input, Car.class);
System.out.println(objectMapper.writeValueAsString(car));

       

序列化代码:

//直接写入文件
ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);
//output:{"color":"yellow","type":"renault"}

//writeValueAsString和writeValueAsBytes方法通过Java Bean生成String或者byte Array类型的JSON。
String carAsString = objectMapper.writeValueAsString(car);

3.配置ObjectMapper的工作方式

1.忽略JSON中多余的属性

有时JSON会拥有比Java对象更多的属性,这种情况下Jackson默认会抛出异常,大概意思是说JSON某个属性未知因为在Java bean中未找到该属性。

然而,有时我们的确会遇到JSON属性比Java对象多的情况。例如我们从REST service获取的JSON会包含更多不需要的属性,这种情况下,Jackson允许通过配置来忽略未知的属性,像这样:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

更多的API可以点进ObjectMapper类查看每个枚举对应的Javadoc

三、Jackson的使用

1.Jackson对于Date类的处理

默认情况下Date类型属性会被序列化long的毫秒数,即timeStamp那种类型的。然而Jackson也支持将Date序列化成特定格式的日期字符串。

public class Transaction {
    /**
     * 单独指定序列化后的格式和反序列化时以此格式来解析日期字符串
     */
    @JsonFormat(pattern = "yyyyMMdd HH:mm:ssSSS", locale = "zh_CN", timezone = "GMT+8")
    private Date date = null;
    private Date create = null;
    // get set......
}

测试代码:

        Transaction transaction = new Transaction();
        transaction.setCreate(new Date());
        transaction.setDate(new Date());
        String resJson = objectMapper.writeValueAsString(transaction);
        // 默认把时间序列化为long
        // {"date":"20190618 11:33:42845","create":1560828822845}
        System.out.println(resJson);

也可以设置ObjectMapper的DateFormat,可以影响所以以该objectMapper对象序列化的所有Java Bean。注意,在Bean中使用注解@JsonFormat定义的格式优先级大于ObjectMapper的DateFormat。

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE);
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 设置这个会影响所有Bean的Date序列化和反序列化
        objectMapper.setDateFormat(dateFormat);

        resJson = objectMapper.writeValueAsString(transaction);
        // {"date":"20190618 11:33:42845","create":"2019-06-18 11:33:42"}
        System.out.println(resJson);

        String transactionJsonStr = "{\"type\":\"transfer\",\"date\":\"20190118 14:27:52052\",\"create\":\"2019-01-18 14:27:52\"," +
                "\"transactionTypeEnum\":\"TICKET\",\"transactionType\":2,\"carTypeEnum\":3}";
        transaction = objectMapper.readValue(transactionJsonStr, Transaction.class);
        System.out.println(transaction);

 2.Jackson的树模型JsonNode

Jackson内置有树模型用来代表JSON对象,这个树模型具体有什么用呢?当你不知道要解析的JSON的结构你就会发现它会很有用,或者由于某种原因你没法创建一个类来表示这个JSON对象。另外一个好处是你可以在使用这个JSON对象之前操纵这个JSON对象,核心类是JsonNode。

        String carJson =
                "{ \"brand\" : \"Mercedes\", \"doors\" : 5," +
                        " \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +
                        " \"nestedObject\" : { \"field\" : \"value\" } }";
        JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);
        // 或者
        jsonNode = objectMapper.readTree(carJson);

        // 取JSON属性值
        JsonNode brandNode = jsonNode.get("brand");
        String brand = brandNode.asText();
        System.out.println("brand = " + brand);
        JsonNode doorsNode = jsonNode.get("doors");
        int doors = doorsNode.asInt();
        System.out.println("doors = " + doors);

        // 取JSON数组
        JsonNode jsonArray = jsonNode.get("owners");
        JsonNode jsonArrayNode = jsonArray.get(0);
        String john = jsonArrayNode.asText();
        System.out.println("john = " + john);

        // 取JSON内嵌对象
        JsonNode childNode = jsonNode.get("nestedObject");
        JsonNode childField = childNode.get("field");
        String field = childField.asText();
        System.out.println("field = " + field);

JsonNode类是不可变的,意味着我们不能直接创建该类的对象,然而可以使用JsonNode的子类ObjectNode来构建对象:

        // 创建ObjectNode
        ObjectNode objectNode = objectMapper.createObjectNode();
        objectNode.put("brand", "Mercedes");
        objectNode.put("doors", 5);
        ObjectNode nestNode = objectMapper.createObjectNode();
        nestNode.put("field", "value");
        objectNode.set("nestedObject", nestNode);
        System.out.println(objectMapper.writeValueAsString(objectNode));

 3.Jackson注解

主要可分为三类:

读写注解(同时影响序列化和反序列化过程)

  • @JsonIgnore  用于需要忽略的属性(不参与序列化和反序列化)
  • @JsonIgnoreProperties  用于需要忽略的一系列属性
  • @JsonIgnoreType   表明所有用到该注解标注的类的地方都会被忽略
  • @JsonAutoDetect   用于检测需要将哪类访问权限的属性参与序列化和反序列化
  • @JsonProperty    表明序列化和反序列化时以注解指明的名称为准,即可以修改bean到JSON中的字段名称
  • @JsonTypeInfo    
  • @JsonSubTypes   这两个主要用于处理多态下反序列化时指定真正的类对象。

读注解(只影响反序列化过程)

  • @JsonSetter 用于表明set方法应该匹配的JSON的属性,当Java bean的属性名和JSON的属性名不一致时就可以使用此注解
  • @JsonAnySetter  用于指明将所有JSON未知的属性都收集在一起
  • @JsonCreator  用于表明Java bean有一个构造方法能够匹配JSON的属性和bean的属性,对于一些没有set方法的bean(不可变对象)来说这个注解是很有用的。
  • @JsonDeserialize   用于定制bean属性的反序列化过程

写注解(只影响序列化过程)

  • @JsonInclude  用于表明值满足特定条件的属性才会被序列化
  • @JsonGetter   用于指明序列化后的属性名而不是用bean的字段名
  • @JsonAnyGetter   能够让你使用一个Map作为容器包含任何你想要序列化的属性
  • @JsonPropertyOrder   用于指明bean属性序列化后在JSON中的顺序
  • @JsonRawValue   用于表明属性值不做任何处理原样输出,String类型的值序列化后会被双引号括住,使用此注解后将不会加上双引号。   主要保证本身就是一个JSON的字符串序列化后不出错。
  • @JsonValue   表明Jackson不做序列化,由此注解标注的方法完成序列化工作。一种轻量的定制序列化方式。
  • @JsonSerialize  用于定制bean属性的序列化过程

demo请参考:https://zhuanlan.zhihu.com/p/69531219 

4.定制类的序列化和反序列化 

1.单个属性的定制

如果对于类的少数属性进行定制,可以使用轻量的@JsonSerialize和@JsonDeserialize注解。示例如下:

@JsonSerialize

public class PersonSerializer {
   private long personId = 0;
   private String name = "John";
   // 这里希望把false序列化成0,true序列化成1
   @JsonSerialize(using = OptimizedBooleanSerializer.class)
   private boolean enabled = false;
   // get set......
}

public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {
    @Override
    public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider)
            throws IOException {
        if (aBoolean) {
            jsonGenerator.writeNumber(1);
        } else {
            jsonGenerator.writeNumber(0);
        }
    }
}

 @JsonDeserialize

public class PersonDeserialize {
   private long id = 0;
   private String name = null;
   // 这里我们想把1映射为true,0映射为false
   @JsonDeserialize(using = OptimizedBooleanDeserializer.class)
   private boolean enabled = false;
   // get set......
}

public class OptimizedBooleanDeserializer extends JsonDeserializer<Boolean> {
    @Override
    public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        String text = jsonParser.getText();
        if ("0".equals(text)) {
            return false;
        } else {
            return true;
        }
    }
}

2.完全定制化整个类的序列化和反序列化

  public static void main(String[] args) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    // 完全定制类的序列化和反序列化过程
    SimpleModule carModule = new CarModule();
    // 注册针对这个类型的处理模块
    objectMapper.registerModule(carModule);
    Car car = new Car();
    car.setBrand("BMW");
    car.setDoors(4);
    String json = objectMapper.writeValueAsString(car);
    System.out.println(json);
    car = objectMapper.readValue(json, Car.class);
    System.out.println(objectMapper.writeValueAsString(car));
  }

 

// 类型处理模块
public class CarModule extends SimpleModule {
    public CarModule() {
        super(PackageVersion.VERSION);
        addDeserializer(Car.class, new CarDeserializer(Car.class));
        addSerializer(Car.class, new CarSerializer(Car.class));
    }
}

// 序列化器
public class CarSerializer extends StdSerializer<Car> {
    private static final long serialVersionUID = 2807109332342106505L;
    public CarSerializer(Class<Car> c) {
        super(c);
    }
    @Override
    public void serialize(Car car, JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider)
            throws IOException {
        jsonGenerator.writeStartObject();
        if (car.getBrand() != null) {
            jsonGenerator.writeStringField("brand", car.getBrand());
        } else {
            jsonGenerator.writeNullField("brand");
        }
        if (car.getDoors() != null) {
            jsonGenerator.writeNumberField("doors", car.getDoors());
        } else {
            jsonGenerator.writeNullField("doors");
        }
        jsonGenerator.writeEndObject();
    }
}

// 反序列化器
public class CarDeserializer extends StdDeserializer<Car> {
    private static final long serialVersionUID = 4977601024588834191L;
    public CarDeserializer(Class<?> c) {
        super(c);
    }
    @Override
    public Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {
        Car car = new Car();
        while (!parser.isClosed()) {
            JsonToken jsonToken = parser.nextToken();
            if (JsonToken.FIELD_NAME == jsonToken) {
                String fieldName = parser.getCurrentName();
                parser.nextToken();
                if ("doors".equals(fieldName)) {
                    car.setDoors(parser.getValueAsInt());
                } else if ("brand".equals(fieldName)) {
                    car.setBrand(parser.getValueAsString());
                }
            }
        }
        return car;
    }
}

需要注意的是这种方式必须将类的所有属性都定制化,否则最终序列化出的结果会缺失没写的那部分。

3.结合SpringMVC的定制化

如果想要在Spring前后台传值中方便的定制化,则可以采用这种方式。

详见:2.在springMVC中的具体应用

四、总结

以上就是Jackson的一些介绍和使用简介。Jackson本身作为Spring框架默认使用的序列化工具类,功能强大社区丰富。本身效率也很高。如果是在开发Spring项目,建议首选。

五、参考文献

1.Jackson,实现Bean和JSON之间的灵活转换(SpringMVC默认的JSON转换器) 

2.JacksonDoc:Tutorials

3.Basic Jackson Marshalling

4.Jackson JSON - Jackson Commonly used Configuration Examples

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值