bingmous

欢迎交流,不吝赐教~

导航

Jackson学习笔记(详细)

学习地址:http://tutorials.jenkov.com/java-json/index.html

github地址:https://github.com/FasterXML/jackson

fasterxml官方地址:http://fasterxml.com/

Jackson ObjectMapper

解析器和生成器

jackson有两个json解析器

  • ObjectMapper将Json解析为Java对象或jackson的tree model
  • JsonParser,一次解析一个token

jackson有两个json生成器

  • ObjectMapper从java对象或tree model生成json
  • JsonGenerator,一次生成一个token

如何映射

jackson通过映射java对象的getter和setter方法实现与json串字段的对应,去掉get set,然后将第一个字母小写,如果要实现别的对应,需要定制序列化与反序列化,或者使用一些jackson注解。

jackson可以读取的内容

jackson读取对象、对象数组可以从

  • json 字符串
  • Reader
  • json file
  • URL,包括HTTP URL
  • InputStream
  • ByteArray
  • 需要使用new TypeReference<List<Car>>(){}的复杂对象的解析
    • 通过json数组读取对象数组
    • 通过json数组读取对象List
    • 通过json字符串读取map

jackson的配置

// 忽略额外的json字段,默认会抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

//null值赋值给java原生类型时抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

定制反序列化器

String json = "{ \"brand\" : \"Ford\", \"doors\" : 6 }";

SimpleModule module =
        new SimpleModule("CarDeserializer", new Version(3, 1, 8, null, null, null));
module.addDeserializer(Car.class, new CarDeserializer(Car.class));

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);

Car car = mapper.readValue(json, Car.class);
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class CarDeserializer extends StdDeserializer<Car> {

    public CarDeserializer(Class<?> vc) {
        super(vc);
    }

    @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.equals(jsonToken)){
                String fieldName = parser.getCurrentName();
                System.out.println(fieldName);

                jsonToken = parser.nextToken();

                if("brand".equals(fieldName)){
                    car.setBrand(parser.getValueAsString());
                } else if ("doors".equals(fieldName)){
                    car.setDoors(parser.getValueAsInt());
                }
            }
        }
        return car;
    }
}

将对象转化为json

  • writeValue()
  • writeValueAsString()
  • writeValueAsBytes()

定制序列化器

CarSerializer carSerializer = new CarSerializer(Car.class);
ObjectMapper objectMapper = new ObjectMapper();

SimpleModule module =
        new SimpleModule("CarSerializer", new Version(2, 1, 3, null, null, null));
module.addSerializer(Car.class, carSerializer);

objectMapper.registerModule(module);

Car car = new Car();
car.setBrand("Mercedes");
car.setDoors(5);

String carJson = objectMapper.writeValueAsString(car);
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class CarSerializer extends StdSerializer<Car> {

    protected CarSerializer(Class<Car> t) {
        super(t);
    }
    public void serialize(Car car, JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider)
            throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("producer", car.getBrand());
        jsonGenerator.writeNumberField("doorCount", car.getDoors());
        jsonGenerator.writeEndObject();
    }
}

修改默认的日期序列化格式

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
objectMapper2.setDateFormat(dateFormat);

String output2 = objectMapper2.writeValueAsString(transaction);
System.out.println(output2);

json树模型 JsonNode类

String carJson =
        "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readTree(carJson);
} catch (IOException e) {
    e.printStackTrace();
}
String carJson =
        "{ \"brand\" : \"Mercedes\", \"doors\" : 5," +
        "  \"owners\" : [\"John\", \"Jack\", \"Jill\"]," +
        "  \"nestedObject\" : { \"field\" : \"value\" } }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);

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

    JsonNode array = jsonNode.get("owners");
    JsonNode jsonNode = array.get(0);
    String john = jsonNode.asText();
    System.out.println("john  = " + john);

    JsonNode child = jsonNode.get("nestedObject");
    JsonNode childField = child.get("field");
    String field = childField.asText();
    System.out.println("field = " + field);
} catch (IOException e) {
    e.printStackTrace();
}

java对象与JsonNode的转换

ObjectMapper objectMapper = new ObjectMapper();

Car car = new Car();
car.brand = "Cadillac";
car.doors = 4;

JsonNode carJsonNode = objectMapper.valueToTree(car);
ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
JsonNode carJsonNode = objectMapper.readTree(carJson);
Car car = objectMapper.treeToValue(carJsonNode);

ObjectMapper读取其他的数据格式

  • CBOR
  • MessagePack
  • YAML
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.IOException;

public class YamlJacksonExample {
    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
        Employee employee = new Employee("John Doe", "john@doe.com");
        String yamlString = null;
        try {
            yamlString = objectMapper.writeValueAsString(employee);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            // normally, rethrow exception here - or don't catch it at all.
        }
        try {
            Employee employee2 = objectMapper.readValue(yamlString, Employee.class);
            System.out.println("Done");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Jackson JsonNode

JsonNode是不可变的,子类ObjectNode是可变的 

将json读取为JsonNode,写为json

JsonNode jsonNode = objectMapper.readTree(json);
String json = objectMapper.writeValueAsString(jsonNode);

获取字段

sonNode jsonNode = ... //parse above JSON into a JsonNode

JsonNode field1 = jsonNode.get("field1");
JsonNode field2 = jsonNode.get("field2");

JsonNode nameNode = jsonNode.at("/identification/name"); //开头必须有/

转化字段的值(注意如果该字段会导致空指针异常)

String f2Str = jsonNode.get("f2").asText();
double f2Dbl = jsonNode.get("f2").asDouble();
int    f2Int = jsonNode.get("f2").asInt();
long   f2Lng = jsonNode.get("f2").asLong();

提供默认值

ObjectMapper objectMapper = new ObjectMapper();
String json = "{ \"f1\":\"Hello\", \"f2\":null }";
JsonNode jsonNode = objectMapper.readTree(json);
String f2Value = jsonNode.get("f2").asText("Default");

判断是否是有效的JsonNode

JsonNode fieldNode = parentNode.get("fieldName");
if(fieldNode == null || fieldNode.isNull()) {
    // the field is either not present in parentNode, or explicitly set to null .
}

递归遍历整个JsonNode

public static void traverse(JsonNode root){
    
    if(root.isObject()){
        Iterator<String> fieldNames = root.fieldNames();

        while(fieldNames.hasNext()) {
            String fieldName = fieldNames.next();
            JsonNode fieldValue = root.get(fieldName);
            traverse(fieldValue);
        }
    } else if(root.isArray()){
        ArrayNode arrayNode = (ArrayNode) root;
        for(int i = 0; i < arrayNode.size(); i++) {
            JsonNode arrayElement = arrayNode.get(i);
            traverse(arrayElement);
        }
    } else {
        // JsonNode root represents a single value field - do something with it.
    }
}

创建ObjectNode,设置值,删除值

//创建
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode parentNode = objectMapper.createObjectNode();

JsonNode childNode = readJsonIntoJsonNode();

parentNode.set("child1", childNode);

//直接赋值原生类型
objectNode.put("field1", "value1");
objectNode.put("field2", 123);
objectNode.put("field3", 999.999);

//删除值
objectNode.remove("fieldName");

遍历JsonNode的字段

Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
    
while(fields.hasNext()) {
    Map.Entry<String, JsonNode> field = fields.next();
    String   fieldName  = field.getKey();
    JsonNode fieldValue = field.getValue();

    System.out.println(fieldName + " = " + fieldValue.asText());
}

遍历字段名

Iterator<String> fieldNames = jsonNode.fieldNames();

while(fieldNames.hasNext()) {
    String fieldName = fieldNames.next();

    JsonNode field = jsonNode.get(fieldName);
}

Jackson JsonParser

JsonParser相对于ObjectMapper是low-level的Json解析器,因此也更快,但也更笨重。通过解析器解析就是将json分解为一个一个的token,然后迭代

创建解析器,解析(也可以直接从objectMapper创建)

String carJson =
        "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";

JsonFactory factory = new JsonFactory();
JsonParser  parser  = factory.createParser(carJson);

Car car = new Car();
while(!parser.isClosed()){
    JsonToken jsonToken = parser.nextToken();

    if(JsonToken.FIELD_NAME.equals(jsonToken)){
        String fieldName = parser.getCurrentName();
        System.out.println(fieldName);

        jsonToken = parser.nextToken();

        if("brand".equals(fieldName)){
            car.brand = parser.getValueAsString();
        } else if ("doors".equals(fieldName)){
            car.doors = parser.getValueAsInt();
        }
    }
}

System.out.println("car.brand = " + car.brand);
System.out.println("car.doors = " + car.doors);

token的类型有以下几种,可以通过这些常量找出当前token的类型

START_OBJECT
END_OBJECT
START_ARRAY
END_ARRAY
FIELD_NAME
VALUE_EMBEDDED_OBJECT
VALUE_FALSE
VALUE_TRUE
VALUE_NULL
VALUE_STRING
VALUE_NUMBER_INT
VALUE_NUMBER_FLOAT

Jackson JsonGenerator

创建Json生成器、生成json

JsonFactory factory = new JsonFactory();

JsonGenerator generator = factory.createGenerator(
    new File("data/output.json"), JsonEncoding.UTF8);

generator.writeStartObject(); //写入{
generator.writeStringField("brand", "Mercedes");
generator.writeNumberField("doors", 5);
generator.writeEndObject(); //写入}

generator.close();

Jackson Annotations

  • Read + Write Annotations
    • @JsonIgnore 忽略该属性的序列化和反序列化
    • @JsonIgnoreProperties 忽略一组属性的序列化和反序列化,放在类上,@JsonIgnoreProperties({"firstName", "lastName"})
    • @JsonIgnoreType 忽略该类型的属性(所有出现的地方)
    • @JsonAutoDetect 包含非public的字段,放在类上,@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY )
  • Read Annotations
    • @JsonSetter 放在setter方法上,修改读取的json字段名,@JsonSetter("id")
    • @JsonAnySetter 对于未识别的Json字段将调用该注解放的set方法上
    • @JsonCreator @JsonAnySetter不可以用的时候用,用该注解标注的构造器创建,也需要标注哪些参数需要构造
    • @JacksonInject 通过注入的方式赋值该字段的值
    • @JsonDeserialize 指定定制的反序列化器
  • Write Annotations
    • @JsonInclude 放在类上,指定非空才序列化@JsonInclude(JsonInclude.Include.NON_EMPTY),
      @JsonInclude(Include.NON_NULL)指定非null才序列化
    • @JsonGetter 修改序列化的字段名
    • @JsonAnyGetter 将该注解标注的方法返回的值其中的kv单独拿出来序列化
    • @JsonPropertyOrder 放在类上,指定字段序列化的顺序
    • @JsonRawValue 该注解标注的字段会去掉字符串的引号进行序列化
    • @JsonValue 序列化时不序列化对象,而是调用该注解标注的方法
    • @JsonSerialize 指定序列化器

总结:

  • Java对象到Json字符串:objectMapper.writeValueAsString(user)
    • 可以定制序列化器
  • Json到Java对象
    • 简单类型:objectMapper.readValue(jsonString, User.class); 
    • 复杂类型:objectMapper.readValue(jsonString, new TypeReference<List<User>>(){});
    • 可以定制反序列化器,Custom Deserializer
  • 核心模块(三个)

 databind依赖于streaming和annotations,使用这一个就可以了

地址:https://github.com/FasterXML/jackson-databind,doc见wiki

  • 介绍

 其他格式的也支持,只要实现了解析器和生成器

mvn仓库

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

如果使用java8的LocalDateTime需要引入jackson-datatype-jsr310(依赖于databind)的maven,且需要使用objectMapper.findAndRegisterModules()代码注册模块。

  • 简单使用

//Java对象转为JSON字符串
user = new User();
objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules(); //使用java8的localDateTime时使用,但是日期还是有问题,需要在日期的属性上加@JsonFormat(paattern = "yyyy-MM-dd HH:mm:ss")
String jsonString = objectMapper.writeValueAsString(user); //返回json字符串


//JSON字符串转为Java对象
objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules(); 
objectMapper.readValue(jsonString, User.class); //简单类型, 有对应的实体类, 如果有泛型会出问题,反序列化的时候会把泛型丢掉, 使用new TypeReference<复杂类型>(){}
objectMapper.readValue(jsonString, new TypeReference<List<User>>(){}); //复杂类型

//修改属性名
@JsonProperty("changedName") //在属性上加

//忽略属性
@JsonIgnore //在属性上加
@JsonIgnoreProperties({"name1", "name2"}) //在类上加

  • 复杂类型使用

    /**
     * 复杂类型转换1
     */
    @Test
    public void test() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        User2 user2 = new User2();
        user2.setId(1001);
        user2.setName("zhangsan");
        ArrayList<Account> accList = new ArrayList<>();
        Account acc1 = new Account(100, "100");
        Account acc2 = new Account(200, "200");
        accList.add(acc1);
        accList.add(acc2);
        HashMap<String, Account> accMap = new HashMap<>();
        accMap.put("zhang10", acc1);
        accMap.put("zhang20", acc2);
        user2.setAccList(accList);
        user2.setAccMap(accMap);
        String s = mapper.writeValueAsString(user2);
        System.out.println(s);
        User2 user21 = mapper.readValue(s, User2.class);
        System.out.println(user21);

    }

    /**
     * 复杂类型转换2
     * List, Map转换, 简单类型可以直接使用,复杂类型使用TypeReference
     */
    @Test
    public void test02() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        ArrayList<User> list = new ArrayList<>();
        User u1 = new User(1001, "zhang1001");
        User u2 = new User(1002, "zhang1002");
        list.add(u1);
        list.add(u2);
        String s = mapper.writeValueAsString(list);
        System.out.println(s);
        List list1 = mapper.readValue(s, List.class);
        System.out.println(list1);
        //
        System.out.println("----------------------");
        HashMap<String, User> map = new HashMap<>();
        map.put("10", u1);
        map.put("20", u2);
        String s1 = mapper.writeValueAsString(map);
        System.out.println(s1);
        Map map1 = mapper.readValue(s1, Map.class); //返回值没有泛型信息
        System.out.println(map1);
        System.out.println("--------");
        Object o = map1.get("10"); //强转报错
//        User o = (User) map1.get("10"); //强转报错
        Map<String, User> map2 = mapper.readValue(s1, new TypeReference<Map<String, User>>() {
        });
        System.out.println(map2);
        User user = map2.get("10");
        System.out.println(user);

    }
  • Tree Model

将Json转化为树结构

    /**
     * 使用 tree 模型, 类似于JSONObject
     */
    @Test
    public void test03() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        User user = new User(1001, null);
        String s = mapper.writeValueAsString(user);
        JsonNode node = mapper.readTree(s);
        System.out.println(node.toString());
        System.out.println("------------");
        JsonNode jsonNode = node.get("id");
        int id = node.get("id").asInt();
        String name = node.get("name").asText();
        String name1 = node.get("name").asText("123");
        System.out.println(name);
        System.out.println(name1);
        System.out.println(id);
    }
  •  Streaming parser, generator

流处理

   /**
     * Streaming parser, generator
     * 将json写入流
     */
    @Test
    public void test04() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        File jsonfile = new File("test.json");
        JsonGenerator g = mapper.createGenerator(jsonfile, JsonEncoding.UTF8);
        g.writeStartObject();
        g.writeStringField("message", "hello world");
        g.writeEndObject();
        g.close();
        boolean newFile = jsonfile.createNewFile(); //写入文件

        //从流写回
        JsonParser p = mapper.createParser(jsonfile);
        JsonToken t = p.nextToken();
        t = p.nextToken();
        if ((t != JsonToken.FIELD_NAME) || (!"message".equals(p.getCurrentName()))) {
            System.out.println("handle error");
        }

        t = p.nextToken();
        if (t != JsonToken.VALUE_STRING) {
            System.out.println("handle error..");
        }

        String text = p.getText();
        System.out.println("my message: " + text);
    }
  • 配置(特性、注解)

所有配置:https://github.com/FasterXML/jackson-databind/wiki/JacksonFeatures

特性配置(higher-level data-binding configuration)

//序列化
// SerializationFeature for changing how JSON is written

// to enable standard indentation ("pretty-printing"):
mapper.enable(SerializationFeature.INDENT_OUTPUT); //开启序列化标准缩进
// to allow serialization of "empty" POJOs (no properties to serialize)
// (without this setting, an exception is thrown in those cases)
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); //关闭空bean序列化异常, 允许空bean的序列化
// to write java.util.Date, Calendar as number (timestamp):
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); //关闭date序列化为时间戳

//反序列化
// DeserializationFeature for changing how JSON is read as POJOs:

// to prevent exception when encountering unknown property:
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); //关闭反序列化未知属性的异常
// to allow coercion of JSON empty String ("") to null Object value:
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); //开启空字符串反序列化为null

特性配置(low-level JSON parsing, generation details) 

//解析json特性
// JsonParser.Feature for configuring parsing settings:

// to allow C/C++ style comments in JSON (non-standard, disabled by default)
// (note: with Jackson 2.5, there is also `mapper.enable(feature)` / `mapper.disable(feature)`)
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); //允许注释的存在
// to allow (non-standard) unquoted field names in JSON:
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); //允许字段名未使用双引号
// to allow use of apostrophes (single quotes), non standard
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); //允许字段名使用单引号


//生成json特性
// JsonGenerator.Feature for configuring low-level JSON generation:

// to force escaping of non-ASCII characters:
mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true); //强制生成非ascii码字符

注解

public class MyBean {
   private String _name;

   // without annotation, we'd get "theName", but we want "name":
   @JsonProperty("name")
   public String getTheName() { return _name; }

   // note: it is enough to add annotation on just getter OR setter;
   // so we can omit it here
   public void setTheName(String n) { _name = n; }
}
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped
// regardless of whether POJO has such properties
@JsonIgnoreProperties({ "foo", "bar" })
public class MyBean
{
   // will not be written as JSON; nor assigned from JSON:
   @JsonIgnore
   public String internal;

   // no annotation, public field is read/written normally
   public String external;

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

   // note: will also be ignored because setter has annotation!
   public int getCode() { return _code; }
}


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

使用定制构造器

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

  @JsonCreator // constructor can be public, private, whatever
  private CtorBean(@JsonProperty("name") String name,
    @JsonProperty("age") int age)
  {
      this.name = name;
      this.age = age;
  }
}

public class FactoryBean
{
    // fields etc omitted for brevity
    @JsonCreator
    public static FactoryBean create(@JsonProperty("name") String name) {
      // construct and return an instance
    }
}
  • POJO到POJO的转换

直接转换,只要to-JSON,from-JSON正常就可以使用

  • 使用建造者模式 + Jackson

在反序列化时指定建造者进行反序列化

 

posted on 2021-09-17 22:58  Bingmous  阅读(655)  评论(0编辑  收藏  举报