fastjson的JSON.toJSONString方法的一些坑

背景

在项目工程为了定位Kafka消费进度的问题,有个字段a类型是Map<TopicPartition, OffsetAndMetadata>,增加打印a字段数据的日志,打印方式是JSON.toJSONString(a),但是日志打印出来的结果却是{{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{}}。

原因


因为TopicPartition的字段都是私有的,而且没有对应的getter方法,json反序列化为了安全考虑,不会反序列化这种字段。因此要想通过JSON.toJSONString打印出想要的字段,就必须将字段改成public或增加getter方法。

测试

定义类:

`

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

public class FastjsonExample {
    public static void main(String[] args) {

        Test1 test1 = new Test1(1, "tom");
        Test2 test2 = new Test2(2, "jack");
        Test3 test3 = new Test3(2, "jack");
        test2.address = "beijing";
        test3.address = "beijing";

        // 作为单独对象
        System.out.println("test1:" + JSON.toJSONString(test1));
        System.out.println("test2:" + JSON.toJSONString(test2));
        System.out.println("test3:" + JSON.toJSONString(test3));

        Map<Test1, Test1> map1 = new HashMap<>();
        map1.put(test1, test1);

        Map<Test2, Test2> map2 = new HashMap<>();
        map2.put(test2, test2);

        Map<Test3, Test3> map3 = new HashMap<>();
        map3.put(test3, test3);

        // 放入map中
        System.out.println("map1:" + JSON.toJSONString(map1));
        System.out.println("map2:" + JSON.toJSONString(map2));
        System.out.println("map3:" + JSON.toJSONString(map3));
        System.out.println("map1 with Feature:" + JSON.toJSONString(map1, JSONWriter.Feature.WriteNonStringKeyAsString, JSONWriter.Feature.WriteNonStringValueAsString));
        System.out.println("map2 with Feature:" + JSON.toJSONString(map2, JSONWriter.Feature.WriteNonStringKeyAsString, JSONWriter.Feature.WriteNonStringValueAsString));
        System.out.println("map3 with Feature:" + JSON.toJSONString(map3, JSONWriter.Feature.WriteNonStringKeyAsString, JSONWriter.Feature.WriteNonStringValueAsString));

    }

    /**
     * 全private字段,且没有getter方法
     */
    static class Test1 implements Serializable {
        private static final long serialVersionUID = 2019555404968089681L;

        public Test1(int offset, String name) {
            this.offset = offset;
            this.name = name;
        }

        private final int offset;
        private final String name;

        @Override
        public String toString() {
            return "ToString{offset=" + offset + ", name='" + name + '}';
        }
    }

    /**
     * 部分private字段,部分public字段,部分getter方法
     */
    static class Test2 implements Serializable {
        private static final long serialVersionUID = 2019555404968089681L;
        public Test2(int offset, String name) {
            this.offset = offset;
            this.name = name;
        }

        private final int offset;
        private final String name;
        public String address;

        public int getOffset() {
            return offset;
        }

        @Override
        public String toString() {
            return "ToString{offset=" + offset + ", name='" + name + ", address='" + address + '}';
        }
    }

    /**
     * 部分private字段,部分public字段,部分getter方法,不重写toString方法
     */
    static class Test3 implements Serializable {
        private static final long serialVersionUID = 2019555404968089681L;
        public Test3(int offset, String name) {
            this.offset = offset;
            this.name = name;
        }

        private final int offset;
        private final String name;
        public String address;

        public int getOffset() {
            return offset;
        }
    }

}

`

结果:

test1:{}
test2:{"address":"beijing","offset":2}
test3:{"address":"beijing","offset":2}
map1:{{}:{}}
map2:{{"address":"beijing","offset":2}:{"address":"beijing","offset":2}}
map3:{{"address":"beijing","offset":2}:{"address":"beijing","offset":2}}
map1 with Feature:{"ToString{offset=1, name='tom}":{}}
map2 with Feature:{"ToString{offset=2, name='jack, address='beijing}":{"address":"beijing","offset":"2"}}
map3 with Feature:{"com.test.fastjson.FastjsonExample$Test3@5a42bbf4":{"address":"beijing","offset":"2"}}

解释

test1结果为空:Test1全private字段,且没有getter方法,因此无法反序列化这些字段。
test2结果中没有name字段:因为Test2的name字段为private字段,且没有getter方法,因此无法反序列化该字段,其他字段正常。
test3结果中没有name字段:原因同上。
map1结果为空:Test1全private字段,且没有getter方法,因此无法反序列化这些字段。
map2结果中没有name字段:因为Test2的name字段为private字段,且没有getter方法,因此无法反序列化该字段,其他字段正常。
map3结果中没有name字段:原因同上。
map1 with Feature结果key为toString方法结果,value为空:使用WriteNonStringKeyAsString特性时,会使用toString方法返回的结果作为key,不会再使用Test1的json字符串作为key;value为空是因为只会使用Test1的json字符串作为value,不会使用toString方法的返回值。
map2 with Feature结果key为toString方法结果,value为json串:原因同上。
map3 with Feature结果key为test3对象的内存地址,value为json串:原因跟上面一样,补充一句:这个key值其实也是toString方法的返回,只是因为Test3没有重写toString方法,所以输出的是对象的内存地址。

结论

现在编程因为注解方便或者习惯化的原因,一般字段都有getter方法,因此我们习惯性觉得JSON.toJSONString方法肯定能把内部的字段值打印出来,但不保证所有的第三方组件都有getter方法,而且无脑打印整个对象的内容也不是一种好的日志输出方式,日志打印应该只打印真正关心的字段值。

posted @ 2025-02-13 18:19  星辰下的键盘  阅读(177)  评论(0)    收藏  举报