zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【安全研究】Jackson 学习笔记

安全笔记学习 研究 jackson
2023-06-13 09:17:53 时间

基本介绍

Jackson框架是基于Java平台的一套数据处理工具,被称为"最好的Java Json解析器",目前Jackson主要有1.x和2.x两个分支版本,其中1.x的类库中包命名以org.codehaus.jackson.xxx开头,2.x类库中包命令com.fastxml.jackson.xxx开头,Jackson框架包含以下3个核心库:

  • streaming
  • databind
  • annotations.Jackson

解析处理

ObjectMapper是Jackson库的主要类,它可以将JSON数据转换成Java对象或者将JAVA对象序列化为JSON数据,ObjectMapper通过JsonParser和JsonGenerator的实例实现JSON实际的读/写

类声明

org.codehaus.jackson.map.ObjectMapper类的声明如下:

public class ObjectMapper
   extends ObjectCodec
      implements Versioned
嵌套类
  • static class ObjectMapper.DefaultTypeResolverBuilder:定制TypeResolverBuilder,提供所谓的"默认输入"使用类型解析构建器
构造函数
  • ObjectMapper():默认的构造函数,使用StdSerializerProvider作为其SerializerProvider,并使用BeanSerializerFactory作为其SerializerFactory
  • ObjectMapper(JsonFactory jf):使用指定的JsonFactory构建必要的JsonParsers和/或JsonGenerators映射
  • ObjectMapper(JsonFactory jf, SerializerProvider sp, DeserializerProvider dp)
  • ObjectMapper(JsonFactory jf, SerializerProvider sp, DeserializerProvider dp, SerializationConfig sconfig, DeserializationConfig dconfig)
  • ObjectMapper(SerializerFactory sf):可以设置序列化工厂setSerializerFactory(org.codehaus.jackson.map.SerializerFactory)
继承的方法

ObjectMapper类继承了java.lang.Objec类方法

数据处理

Json处理方法分类

Jackson支持Java对象与Json之间的相互转化,Java对象序列化为Json字符串,Json字符串也可以反序列化为相同的Java对象,Jackson提供了三种可选的Json处理方法:

  • 树模型(Tree Model):是最灵活的处理方式
  • 流式API(Streaming API):是效率最高的处理方式(开销低、读写速度快,但程序编写复杂度高)
  • 数据绑定(Data Binding):是最常用的处理方式

下面从结合具体的实战来看一下,首先在pom.xml文件中添加以下三个依赖:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.10</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.10</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.10</version>
    </dependency>
树模型(Tree Model)
tree model生成json
package com.jacksonTest;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.File;
import java.io.FileWriter;

public class SerializationExampleTreeModel {
    public static void main(String[] args) throws Exception {
        //创建一个节点工厂,为我们提供所有节点
        JsonNodeFactory factory = new JsonNodeFactory(false);
        //创建一个json factory来写tree    modle为json
        JsonFactory jsonFactory = new JsonFactory();
        //创建一个json生成器
        JsonGenerator generator = jsonFactory.createGenerator(new FileWriter(new File("country2.json")));
        //注意,默认情况下对象映射器不会指定根节点,下面设根节点为country
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode country = factory.objectNode();

        country.put("country_id", "China");
        country.put("birthDate", "1949-10-01");

        //在Java中,List和Array转化为json后对应的格式符号都是"obj:[]"
        ArrayNode nation = factory.arrayNode();
        nation.add("Han").add("Meng").add("Hui").add("WeiWuEr").add("Zang");
        country.set("nation", nation);

        ArrayNode lakes = factory.arrayNode();
        lakes.add("QingHai Lake").add("Poyang Lake").add("Dongting Lake").add("Taihu Lake");
        country.set("lakes", lakes);

        ArrayNode provinces = factory.arrayNode();
        ObjectNode province = factory.objectNode();
        ObjectNode province2 = factory.objectNode();
        province.put("name","Shanxi");
        province.put("population", 37751200);
        province2.put("name","ZheJiang");
        province2.put("population", 55080000);
        provinces.add(province).add(province2);
        country.set("provinces", provinces);

        ObjectNode traffic = factory.objectNode();
        traffic.put("HighWay(KM)", 4240000);
        traffic.put("Train(KM)", 112000);
        country.set("traffic", traffic);

        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.writeTree(generator, country);
    }
}

程序运行生成country2.json:

{"country_id":"China","birthDate":"1949-10-01","nation":["Han","Meng","Hui","WeiWuEr","Zang"],"lakes":["QingHai Lake","Poyang Lake","Dongting Lake","Taihu Lake"],"provinces":[{"name":"Shanxi","population":37751200},{"name":"ZheJiang","population":55080000}],"traffic":{"HighWay(KM)":4240000,"Train(KM)":112000}}

json反序列化为tree mode1

package com.jacksonTest;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.Iterator;

public class DeserializationExampleTreeModel1 {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        // Jackson提供一个树节点被称为"JsonNode",ObjectMapper提供方法来读json作为树的JsonNode根节点
        JsonNode node = mapper.readTree(new File("country2.json"));
        // 看看根节点的类型
        System.out.println("node JsonNodeType:"+node.getNodeType());
        // 是不是一个容器
        System.out.println("node is container Node ? "+node.isContainerNode());
        // 得到所有node节点的子节点名称
        System.out.println("---------得到所有node节点的子节点名称-------------------------");
        Iterator<String> fieldNames = node.fieldNames();
        while (fieldNames.hasNext()) {
            String fieldName = fieldNames.next();
            System.out.print(fieldName+" ");
        }
        System.out.println("\n-----------------------------------------------------");
        // as.Text的作用是有值返回值,无值返回空字符串
        JsonNode country_id = node.get("country_id");
        System.out.println("country_id:"+country_id.asText() + " JsonNodeType:"+country_id.getNodeType());

        JsonNode birthDate = node.get("birthDate");
        System.out.println("birthDate:"+birthDate.asText()+" JsonNodeType:"+birthDate.getNodeType());

        JsonNode nation = node.get("nation");
        System.out.println("nation:"+ nation+ " JsonNodeType:"+nation.getNodeType());

        JsonNode lakes = node.get("lakes");
        System.out.println("lakes:"+lakes+" JsonNodeType:"+lakes.getNodeType());

        JsonNode provinces = node.get("provinces");
        System.out.println("provinces JsonNodeType:"+provinces.getNodeType());

        boolean flag = true;
        for (JsonNode provinceElements : provinces) {
            //为了避免provinceElements多次打印,用flag控制,打印能体现provinceElements的JsonNodeType就可以了
            if(flag){
                System.out.println("provinceElements JsonNodeType:"+provinceElements.getNodeType());
                System.out.println("provinceElements is container node? "+provinceElements.isContainerNode());
                flag = false;
            }
            Iterator<String> provinceElementFields = provinceElements.fieldNames();
            while (provinceElementFields.hasNext()) {
                String fieldName = (String) provinceElementFields.next();
                String province;
                if ("population".equals(fieldName)) {
                    province = fieldName + ":" + provinceElements.get(fieldName).asInt();
                }else{
                    province = fieldName + ":" + provinceElements.get(fieldName).asText();
                }
                System.out.println(province);
            }
        }
    }
}

程序运行后打印结果如下:

json反序列化为tree mode2

再来看一下DeserializationExampleTreeModel2.java,本例中使用JsonNode.path的方法,path方法类似于DeserializationExampleTreeModel1.java中使用的get方法, 但当node不存在时get方法返回null,而path返回MISSING类型的JsonNode

package com.jacksonTest;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;

public class DeserializationExampleTreeModle2 {
    public static void main(String[] args) throws JsonProcessingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node = mapper.readTree(new File("country2.json"));
        //path方法获取JsonNode时,当对象不存在时,返回MISSING类型的JsonNode
        JsonNode missingNode = node.path("test");
        if(missingNode.isMissingNode()){
            System.out.println("JsonNodeType : " + missingNode.getNodeType());
        }

        System.out.println("country_id:"+node.path("country_id").asText());

        JsonNode provinces = node.path("provinces");
        for (JsonNode provinceElements : provinces) {
            Iterator<String> provincesFields = provinceElements.fieldNames();
            while (provincesFields.hasNext()) {
                String fieldName = (String) provincesFields.next();
                String province;
                if("name".equals(fieldName)){
                    province = fieldName +":"+ provinceElements.path(fieldName).asText();
                }else{
                    province = fieldName +":"+ provinceElements.path(fieldName).asInt();
                }
                System.out.println(province);
            }
        }
    }
}

程序运行打印结果:

流式API(Streaming API)
Stream生成json
package com.jacksonTest;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;

import java.io.File;
import java.io.FileWriter;

public class StreamGeneratorJson {
    public static void main(String[] args) throws Exception {
        JsonFactory factory = new JsonFactory();
        //从JsonFactory创建一个JsonGenerator生成器的实例
        JsonGenerator generator = factory.createGenerator(new FileWriter(new File("country3.json")));

        generator.writeStartObject();
        generator.writeFieldName("country_id");
        generator.writeString("China");
        generator.writeFieldName("provinces");
        generator.writeStartArray();
        generator.writeStartObject();
        generator.writeStringField("name", "Shanxi");
        generator.writeNumberField("population", 33750000);
        generator.writeEndObject();
        generator.writeEndArray();
        generator.writeEndObject();
        generator.close();
    }
}

程序运行后生成country3.json文件内容:

stream解析json

现在我们用Streaming API的方式来解析上面的Json,并查找json中population的值:

package com.jacksonTest;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

import java.io.File;
import java.io.IOException;

public class StreamParserJson {
    public static void main(String[] args) throws JsonParseException, IOException {
        JsonFactory factory = new JsonFactory();
        // 从JsonFactory创建JsonParser解析器的实例
        JsonParser parser = factory.createParser(new File("country3.json"));
        while (!parser.isClosed()) {
            // 得到一个token,第一次遍历时,token指向json文件中第一个符号"{"
            JsonToken token = parser.nextToken();
            if (token == null) {
                break;
            }
            // 我们只查找 country3.json中的"population"字段的值,能体现解析的流程就可以了
            // 当key是provinces时,我们进入provinces,查找population
            if (JsonToken.FIELD_NAME.equals(token)&& "provinces".equals(parser.getCurrentName())) {
                token = parser.nextToken();
                if (!JsonToken.START_ARRAY.equals(token)) {
                    break;
                }
                // 此时,token指向的应该是"{"
                token = parser.nextToken();
                if (!JsonToken.START_OBJECT.equals(token)) {
                    break;
                }
                while (true) {
                    token = parser.nextToken();
                    if (token == null) {
                        break;
                    }
                    if ( JsonToken.FIELD_NAME.equals(token)
                            &&"population".equals(parser.getCurrentName())) {
                        token = parser.nextToken();
                        System.out.println(parser.getCurrentName() + " : "
                                + parser.getIntValue());
                    }
                }
            }
        }
    }
}

程序运行后,在控制台打印结果如下:

数据绑定(Data Binding)
数据绑定简介

数据绑定API用于JSON转换和使用属性访问或使用注解POJO(普通Java对象),以下是它的两个类型:

  • 简单数据绑定 - 转换JSON,从Java Maps, Lists, Strings, Numbers, Booleans和null对象
  • 完整数据绑定 - 转换JSON到任何JAVA类型

ObjectMapper读/写JSON两种类型的数据绑定,数据绑定是最方便的方式是类似XML的JAXB解析器

简单数据绑定

简单的数据绑定是指JSON映射到Java核心数据类型,下表列出了JSON类型和Java类型之间的关系:

具体代码示例:

package com.jacksonTest;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class JacksonTester {
    public static void main(String args[]){
        JacksonTester tester = new JacksonTester();
        try {
            ObjectMapper mapper = new ObjectMapper();

            Map<String,Object> studentDataMap = new HashMap<String,Object>();
            int[] marks = {1,2,3};

            Student student = new Student();
            student.setAge(10);
            student.setName("Mahesh");
            // JAVA Object
            studentDataMap.put("student", student);
            // JAVA String
            studentDataMap.put("name", "Mahesh Kumar");
            // JAVA Boolean
            studentDataMap.put("verified", Boolean.FALSE);
            // Array
            studentDataMap.put("marks", marks);

            mapper.writeValue(new File("student.json"), studentDataMap);
            //result student.json
            //{
            //   "student":{"name":"Mahesh","age":10},
            //   "marks":[1,2,3],
            //   "verified":false,
            //   "name":"Mahesh Kumar"
            //}
            studentDataMap = mapper.readValue(new File("student.json"), Map.class);

            System.out.println(studentDataMap.get("student"));
            System.out.println(studentDataMap.get("name"));
            System.out.println(studentDataMap.get("verified"));
            System.out.println(studentDataMap.get("marks"));
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class Student {
    private String name;
    private int age;
    public Student(){}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String toString(){
        return "Student [ name: "+name+", age: "+ age+ " ]";
    }
}

运行结果如下所示:

完全数据绑定

完全数据绑定是指JSON映射到任何Java对象,例如:

//Create an ObjectMapper instance
ObjectMapper mapper = new ObjectMapper();  

//map JSON content to Student object
Student student = mapper.readValue(new File("student.json"), Student.class);

//map Student object to JSON content
mapper.writeValue(new File("student.json"), student);

具体代码示例:

package com.jacksonTest;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.IOException;

public class JacksonTester {
    public static void main(String args[]){
        JacksonTester tester = new JacksonTester();
        try {
            Student student = new Student();
            student.setAge(10);
            student.setName("Mahesh");
            tester.writeJSON(student);

            Student student1 = tester.readJSON();
            System.out.println(student1);

        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void writeJSON(Student student) throws JsonGenerationException, JsonMappingException, IOException{
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(new File("student.json"), student);
    }

    private Student readJSON() throws JsonParseException, JsonMappingException, IOException{
        ObjectMapper mapper = new ObjectMapper();
        Student student = mapper.readValue(new File("student.json"), Student.class);
        return student;
    }
}

class Student {
    private String name;
    private int age;
    public Student(){}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String toString(){
        return "Student [ name: "+name+", age: "+ age+ " ]";
    }
}

运行结果如下所示:

java对象序列化为json

Province.java:

package com.jacksonTest;

public class Province {
    public String name;
    public int population;
    public String[] city;
}

Country.java:

package com.jacksonTest;

import java.util.*;

public class Country {
    // 注意:被序列化的bean的private属性字段需要创建getter方法或者属性字段应该为public
    private String country_id;
    private Date birthDate;
    private List<String> nation = new ArrayList<String>();
    private String[] lakes;
    private List<Province> provinces = new ArrayList<Province>();
    private Map<String, Integer> traffic = new HashMap<String, Integer>();
    
    public Country() {
        // TODO Auto-generated constructor stub
    }

    public Country(String countryId) {
        this.country_id = countryId;
    }

    public String getCountry_id() {
        return country_id;
    }

    public void setCountry_id(String country_id) {
        this.country_id = country_id;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public List<String> getNation() {
        return nation;
    }

    public void setNation(List<String> nation) {
        this.nation = nation;
    }

    public String[] getLakes() {
        return lakes;
    }

    public void setLakes(String[] lakes) {
        this.lakes = lakes;
    }

    public Integer get(String key) {
        return traffic.get(key);
    }

    public Map<String, Integer> getTraffic() {
        return traffic;
    }

    public void setTraffic(Map<String, Integer> traffic) {
        this.traffic = traffic;
    }

    public void addTraffic(String key, Integer value) {
        traffic.put(key, value);
    }

    public List<Province> getProvinces() {
        return provinces;
    }

    public void setProvinces(List<Province> provinces) {
        this.provinces = provinces;
    }

    @Override
    public String toString() {
        return "Country [country_id=" + country_id + ", birthDate=" + birthDate
                + ", nation=" + nation + ", lakes=" + Arrays.toString(lakes)
                + ", province=" + provinces + ", traffic=" + traffic + "]";
    }
}

JavaBeanSerializeToJson.java

package com.jacksonTest;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

public class JavaBeanSerializeToJson {
    public static void convert() throws Exception {
        // 使用ObjectMapper来转化对象为Json
        ObjectMapper mapper = new ObjectMapper();
        // 添加功能,让时间格式更具有可读性
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        mapper.setDateFormat(dateFormat);

        Country country = new Country("China");
        country.setBirthDate(dateFormat.parse("1949-10-01"));
        country.setLakes(new String[] { "Qinghai Lake", "Poyang Lake",
                "Dongting Lake", "Taihu Lake"});

        List<String> nation = new ArrayList<String>();
        nation.add("Han");
        nation.add("Meng");
        nation.add("Hui");
        nation.add("WeiWuEr");
        nation.add("Zang");
        country.setNation(nation);

        Province province = new Province();
        province.name = "Shanxi";
        province.population = 37751200;
        
        Province province2 = new Province();
        province2.name = "ZheJiang";
        province2.population = 55080000;
        
        List<Province> provinces = new ArrayList<Province>();
        provinces.add(province);
        provinces.add(province2);
        country.setProvinces(provinces);

        country.addTraffic("Train(KM)", 112000);
        country.addTraffic("HighWay(KM)", 4240000);
        // 为了使JSON视觉上的可读性,增加一行如下代码,注意,在生产中不需要这样,因为这样会增大Json的内容
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        // 配置mapper忽略空属性
        mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        // 默认情况,Jackson使用Java属性字段名称作为 Json的属性名称,也可以使用Jackson annotations(注解)改变Json属性名称
        mapper.writeValue(new File("country.json"), country);
    }

    public static void main(String[] args) throws Exception {
        convert();
    }
}

程序运行后生成country.json:

Json反序列化为java对象

package com.jacksonTest;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.List;

public class JsonDeserializeToJava {
    public static void main(String[] args) throws Exception {
        //ObjectMapper类用序列化与反序列化映射器  
        ObjectMapper mapper = new ObjectMapper();
        File json = new File("country.json");
        //当反序列化json时,未知属性会引起的反序列化被打断,这里我们禁用未知属性打断反序列化功能,  
        //因为,例如json里有10个属性,而我们的bean中只定义了2个属性,其它8个属性将被忽略  
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        //从json映射到java对象,得到country对象后就可以遍历查找,下面遍历部分内容,能说明问题就可以了  
        Country country = mapper.readValue(json, Country.class);
        System.out.println("country_id:"+country.getCountry_id());
        //设置时间格式,便于阅读  
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
        String birthDate = dateformat.format(country.getBirthDate());
        System.out.println("birthDate:"+birthDate);

        List<Province> provinces = country.getProvinces();
        for (Province province : provinces) {
            System.out.println("province:"+province.name + "\n" + "population:"+province.population);
        }
    }
}

程序运行结果:

ObjectMapper使用特性

在默认情况下ObjectMapper在序列化时会将所有的字段逐一序列化,无论这些字段是否有值,或者为null,另外序列化依赖于getter方法,如果某个字段没有getter方法,那么该字段是不会被序列化的,由此可见在序列化时OjbectMapper是通过反射机制找到了对应的getter,然后将getter方法对应的字段序列化到Json中~