常用开发库 - JSON库详解

arcstack2023年5月26日约 3377 字大约 11 分钟

常用开发库 - JSON库详解

JSON应用非常广泛,对于Java常用的JSON库要完全掌握; 其中考虑到FastJson代码质量,漏洞,坑等等,应该尽量避免使用。@pdai

JSON简介

JSON是什么

结构与类型

一个简单实例

    {
       "Image": {
           "Width":  800,
           "Height": 600,
           "Title":  "View from 15th Floor",
           "Thumbnail": {
               "Url":    "http://www.example.com/image/481989943",
               "Height": 125,
               "Width":  "100"
           },
           "IDs": [116, 943, 234, 38793]
         }
    }

JSON优秀资源

JSON在线解析工具

dev-package-json-3.png
dev-package-json-3.png

JSON类库

Java中并没有内置JSON的解析,因此使用JSON需要借助第三方类库。 下面是几个常用的 JSON 解析类库:

性能测试对比

从下面的测试结果可以看出,序列化次数比较小的时候,Gson性能最好,当不断增加的时候到了100000,Gson明细弱于Jackson和FastJson, 这时候FastJson性能是真的牛,另外还可以看到不管数量少还是多,Jackson一直表现优异。而那个Json-lib可以直接忽略。

dev-package-json-1.png
dev-package-json-1.png

dev-package-json-2.png 更多请参考: Java几种常用JSON库性能比较open in new window

FastJson

先泼一盆冷水,个人非常不推荐使用FastJson, 为什么?

Fastjson 简介

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。 Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。

Fastjson 特性

下载和使用

你可以在 maven 中央仓库中直接下载:http://repo1.maven.org/maven2/com/alibaba/fastjson/open in new window 配置 maven 依赖:

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>x.x.x</version>
    </dependency>

其中 x.x.x 是版本号,根据需要使用特定版本,建议使用最新版本。

序列化一个对象成JSON字符串

    User user = new User();
    user.setName("校长");
    user.setAge(3);
    user.setSalary(new BigDecimal("123456789.0123"));
    String jsonString = JSON.toJSONString(user);
    System.out.println(jsonString);
    // 输出 {"age":3,"name":"校长","old":false,"salary":123456789.0123}

反序列化一个JSON字符串成Java对象

    String jsonString = "{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校长\",\"old\":true,\"salary\":123456789.0123}";
     User u = JSON.parseObject(jsonString ,User.class);
     System.out.println(u.getName());
     // 输出 校长

    String jsonStringArray = "[{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校长\",\"old\":true,\"salary\":123456789.0123}]";
    List<User> userList = JSON.parseArray(jsonStringArray, User.class);
    System.out.println(userList.size());
    // 输出 1

对于日期的处理

默认序列化Date输出使用”yyyy-MM-dd HH:mm:ss”格式,可以用UseISO8601DateFormat特性换成”yyyy-MM-dd’T’HH:mm:ss”格式。

    JSON.defaultTimeZone = TimeZone.getTimeZone("Asia/Shanghai");
    JSON.defaultLocale = Locale.US;
            
    public static class Model {
        @JSONField(format = "MMM dd, yyyy h:mm:ss aa")
        private java.util.Date date;

        public java.util.Date getDate() {
            return date;
        }

        public void setDate(java.util.Date date) {
            this.date = date;
        }

        @JSONField(format = "MMM-dd-yyyy h:mm:ss aa")
        public java.sql.Date date2;
    }

Bean和数组转换

设置字段名

    public class A {
        @JSONField(name="ID")
        private int id;

        public int getId() {return id;}
        public void setId(int value) {this.id = id;}
    }

设置是否不序列化某字段

    public class A {
        @JSONField(serialize=false)
        public Date date;
    }

    public class A {
        @JSONField(deserialize=false)
        public Date date;
    }

设置字段顺序

    public static class VO {
        @JSONField(ordinal = 3)
        private int f0;

        @JSONField(ordinal = 2)
        private int f1;

        @JSONField(ordinal = 1)
        private int f2;
    }

自定义序列化和反序列化

FastJson漏洞问题

尽量使用最新版本。

好了,我要开喷了。 警告 远离FastJson这个库,老程序员都知道这里有多少坑:

JackSon

JackSon简介

Jackson组件

3个核心模块:

第三方数据类型模块

这些扩展是插件式的Jackson模块,用ObjectMapper.registerModule()注册,并且通过添加serializers和deserializers以便Databind包(ObjectMapper / ObjectReader / ObjectWriter)可以读写这些类型,来增加对各种常用的Java库的数据类型的支持。

数据格式模块

Jackson也有处理程序对JAX-RS标准实现者例如Jersey, RESTeasy, CXF等提供了数据格式支持。处理程序实现了MessageBodyReader和MessageBodyWriter,目前支持的数据格式包括JSON, Smile, XML, YAML和CBOR。 数据格式提供了除了Json之外的数据格式支持,它们绝大部分仅仅实现了streaming API abstractions,以便数据绑定组件可以按照原来的方式使用。另一些(几乎不需要)提供了databind标准功能来处理例如schemas。

Jackson的使用

引用maven jar包:

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

序列化一个对象成JSON字符串

    public void toJson() throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();

        City case1 = new City();
        case1.setCity("SZ");
        case1.setAge(123);

        String jsonStr = mapper.writeValueAsString(case1);
        System.out.println("JSON:" + jsonStr);
    }
    // 输出:JSON:{"city":"SZ","age":123}

反序列化一个JSON字符串成Java对象

    public void toObj() throws JsonParseException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        String inputjsonstr = "{\"city\":\"SZ\",\"age\":123}";
        
        City readcase = mapper.readValue(inputjsonstr, City.class);

        System.out.println("city info:" + readcase);
    }

如果里面有未知属性,比如json中有desc字段,但是City中没有相应字段,会报错, 需要设置如下:

    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

常用注解

GSON

Gson简介

Gson是这样一个Java类库,它可以将Java对象转换为相应的JSON形式,也可以将JSON字符串转换为对应的Java对象。 Gson可以使用任意Java对象,包括哪些预先存在的、不在你的源代码中的对象(因此,你并不知道对象的属性)。

Gson的目标

Gson的使用

使用Gson的首要类是Gson类,你可以仅仅通过new Gson()的方式创建它。你也可以通过GsonBuilder类去创建Gson实例,这个类允许你进行一系列配置,例如版本控制等等。 Gson实例不会保存任何进行Json操作时的状态。因此,你可以自由的服用相同的Gson对象进行诸多的Json序列化和反序列化操作。 引用maven jar包:

    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>28.2-jre</version>
    </dependency>

序列化

    // 基础类型
    Gson gson = new Gson();
    gson.toJson(1);            ==> prints 1
    gson.toJson("abcd");       ==> prints "abcd"
    gson.toJson(new Long(10)); ==> prints 10
    int[] values = { 1 };
    gson.toJson(values);       ==> prints [1]

    // 对象
    BagOfPrimitives obj = new BagOfPrimitives();
    Gson gson = new Gson();
    String json = gson.toJson(obj);  
    ==> json is {"value1":1,"value2":"abc"}

    // 数组
    Gson gson = new Gson();
    int[] ints = {1, 2, 3, 4, 5};
    String[] strings = {"abc", "def", "ghi"};
    gson.toJson(ints);     ==> prints [1,2,3,4,5]
    gson.toJson(strings);  ==> prints ["abc", "def", "ghi"]

    // 集合
    Gson gson = new Gson();
    Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);
    String json = gson.toJson(ints); ==> json is [1,2,3,4,5]

其中的对象代码:

    class BagOfPrimitives {
      private int value1 = 1;
      private String value2 = "abc";
      private transient int value3 = 3;
      BagOfPrimitives() {
        // no-args constructor
      }
    }

反序列化

    // 基础类型
    Gson gson = new Gson();
    int one = gson.fromJson("1", int.class);
    Integer one = gson.fromJson("1", Integer.class);
    Long one = gson.fromJson("1", Long.class);
    Boolean false = gson.fromJson("false", Boolean.class);
    String str = gson.fromJson("\"abc\"", String.class);
    String anotherStr = gson.fromJson("[\"abc\"]", String.class);

    // 对象
    BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);   
    ==> obj2 is just like obj

    // 数组
    Gson gson = new Gson();
    int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
    ==> ints2 will be same as ints

    // 集合
    Gson gson = new Gson();
    Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
    Collection<Integer> ints2 = gson.fromJson(json, collectionType);
    ints2 is same as ints

自定义序列化和反序列化机制

有时候,默认的实现并不是你想要的。这在处理类库时常常发生(例如DateTime)。Gson允许你注册自己自定义的序列化器和反序列化器。该过程分为两部分:

实例构造者:并不需要,如果无参构造器是可用的或者注册了一个反序列化器。

    GsonBuilder gson = new GsonBuilder();
    gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
    gson.registerTypeAdapter(MyType.class, new MySerializer());
    gson.registerTypeAdapter(MyType.class, new MyDeserializer());
    gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());

registerTypeAdapter会检查类型适配器是否实现了上面三个接口中的一个以上并且它们都注册了类型适配器。 更多请参考:Gson用户指南(中文翻译)open in new window

参考文章