fastjson那些事

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. FASTJSON 那些事 阿里中间件 温绍锦 2017.07.26 为了无法计算的价值
2. FASTJSON 发展历程 2011 2017.1 开源 & 发展 安全漏洞 2011.1 开源到 alibabatech.org 2011.7 开源到 github.com/alibaba github.com/eishay/jvm-serializers 评测 json/databind 分类最快 json-lib 太慢 Jackson-1.x API 不友好 Jackson-1.x LGPG 协议 项目创建 2010.12 2017.1.27 除夕确认漏洞开始修复 2017.3.15 内部修复完毕对外公告 漏洞披露规范未造成严重影响 2012 年最受欢迎中国开源软件 2013 年开源中国 10 大热门开源项目 2016 年最受欢迎中国开源软件 2016 年发布 1.1.52.android 针对 android 做性能优化 2017 年 maven 中央仓库月下载超过 15 万 被广泛采用 2012~2017 为了无法计算的价值
3. 协议类型 序列化 反序列化 json/dsl-platform 文本 526 806 Eishay 性能对 比 json-array/fastjson/databind 文本 650 696 基于数组性能极致 msgpack/databind 二进制 796 1052 protobuf 二进制 1173 719 json/fastjson/databind 文本 1058 1241 thrift 二进制 1455 731 json/jackson/databind 文本 1164 1866 hessian 文本 2842 4622 json/gson/databind 文本 4667 4403 bson/jackson/databind 二进制 4105 5449 java-built-in 二进制 5046 23279 json/json-lib/databind 文本 19853 71969 数据来源 https://github.com/eishay/jvm-serializers/wiki json/databind 分类最 快 比流行的二进制协议快! 为了无法计算的价值
4. Java JSON 库同类产品对比 fastjson fastjson-android jackson gson moshi 最新版本 1.2.35 1.1.60.android 2.8.9 2.8.1 1.5.0 最新版大小 481K 215K 1580K 232K 124K + 81K 性能 非常好 Android 下非常好 好 一般 一般 github star 9932 3084 9523 2911 github fork 3377 781 2163 220 支持 JSONPath 支持 不支持 不支持 不支持 支持泛型 支持 支持 支持 支持循环引用 支持 不支持 不支持 不支持 不支持 为了无法计算的价值
5. json/json-lib/databind 71969 19853 java-built-in 23279 5046 5449 4105 bson/jackson/databind json/gson/databind 4403 4667 hessian 4622 2842 json/jackson/databind thrift 1866 1164 731 1455 1241 1058 json/fastjson/databind protobuf 719 1173 1052 796 msgpack/databind json-array/fastjson/databind 696 650 806 526 json/dsl-platform 0 10000 20000 序列化 30000 反序列化 40000 50000 60000 70000 80000
6. 1800 1580 1600 1400 1200 1000 800 600 481 400 232 205 gson 214 moshi 200 0 fastjson fastjson-android jackson 最新版大小 (K)K) 12000 10000 9916 9506 8000 6000 4000 3070 2911 2000 0 fastjson jackson github star gson github fork moshi
7. Android 环境性能对比 • 测试环境 - 华为 P10 (低端手机环境低端手机环境 fastjson 会更有优势) • 数值为耗时,单位毫秒,越小越好 • 内置 org.json 在某些机型下性能会较好,但都不如 fastjson • 首次序列化 / 反序列化在 fastjson 每次 new ParserConfig/SerializeConfig , jackson 每次 new ObjectMapper , gson 每次 new Gson fastjson-1.1.60.android gson-2.8.1 内置 org.json 首次序列化 10000 次 1204 2925 14253 首次反序列化 10000 次 2436 3284 20475 序列化 100000 次 1245 4822 2551 1864 反序列化 100000 次 1672 3179 3441 2765 TradeObjectParse 26k1000 次 956 985 1982 1161 CartObjectParse-70k1000 次 2375 2034 5039 在 Android 下绝对优势碾压其他 JSON 库,针对首次优化对首屏快速展示效果非常好 moshi-1.5.0 jackson-2.8.9 2449 为了无法计算的价值
8. 首次序列化 10000 次 首次反序列化 10000 次 16000 25000 14000 20000 12000 10000 15000 8000 10000 6000 4000 5000 2000 0 fastjson-1.1.60.android gson-2.8.1 0 jackson-2.8.9 fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9 反序列化 100000 次 序列化 100000 次 4000 6000 3500 5000 3000 4000 2500 2000 3000 1500 2000 1000 1000 0 500 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 0 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 CartObjectParse-70k1000 次 TradeObjectParse 26k1000 次 2500 6000 2000 5000 4000 1500 3000 1000 2000 500 0 1000 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 0 fastjson-1.1.60.android gson-2.8.1 内置 org.json jackson-2.8.9
9. FASTJSON 的优点 功能完备 性能最好 支持泛型、 JDK8 第三方评测性能最好 支持 JSONPath Android 4/5/6/7 下性能最好 活跃受欢迎 要想写什 么就写什 么吧! 稳定 Github 上同类产品 Star 最多 回归测试 3869 个 多年开源中国最受欢迎开源软件评比在前 10 测试覆盖率 86% 开源在 github 上 https://github.com/alibaba/fastjson Maven 仓库下载地址 http://repo1.maven.org/maven2/com/alibaba/fastjson/ 为了无法计算的价值
10. 1/3 性能优化技术 1 2 3 使用 ThreadLocal 在 SerializeWriter/JSONScanner 中使用 ThreadLocal 保存一 个 byte[]/char[] ,减少内存分配 SymbolTable 一个特别优化过的 HashMap ,将常用的 Name 保存起来,不 用每次分配 IdentityHashMap 能避免并发导致的死锁和正确性问题的 IdentityHashMap 4 5 6 缓存 Method/Field/Constructor 在 ParserConfig/SerializeConfig 中保存 Class/Method/Field/ Constructor ,降低反射开销 针对 int/long 序列化的优化 针对 int/long 类型序列化做特别优化, 避免 toString+WriteString 减少对象分配 针对 int/long 反序列化的优化 参考自 Integer.parseInt Integer.parseInt 不支持输入为 char[] ,支持 String 类型参数 针对 10 进制优化 为了无法计算的价值
11. 2/3 性能优化技术 7 8 9 针对 float/double 反序列化的优化 Float/Doubble.parse 方法很慢 VR 场景有大量的 float/double encodeUTF8/decodeUTF8 优化 参考 sun.nio.cs.ArrayEncoder/ sun.nio.cs.ArrayDecoder 使用 asm 动态生成 serializer/deserializer 内置 asm ,基于 objectweb asm 3.3 改造,只保留必要部分, 不到 2000 行代码 10 11 12 小方法手动内联 Android 方法调用开销大 Oracle HotSpot 的内联效果也不够好 直接访问 Field 比访问 getter/setter 更好 快速匹配算法 Fastjson 独创的提升 json 反序列化性能的算法 基于字节码动态生成实现 性能超越 jackson 的关键 fnv_hash 匹配优化 来自 json/dsl-platform 的优化算法 用于 android 版本性能优化 为了无法计算的价值
12. 3/3 性能优化技术 13 14 15 BeanToArray 超强性能模式 超越大多数二进制协议 兼顾性能和可维护性 Enum Parse 优化 避免 Name 对象创建 字符串遍历优化 兼顾 HotSpot 和 Android Dalvik 的最优解法 16 17 18 BitFlags 一个 Int 表示 32 个选项 结合 Enum 的 ordinal JVM Instrinstic 利用 JVM Intrinsic 方法优化 日期类型 Parse 优化 SimpleDateFormat 线程并不安全 SimpleDateFormat 一次只能匹配一种格式 为了无法计算的价值
13. public class SerializeWriter { static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>(); char[] buf; public SerializeWriter() { buf = bufLocal.get(); if (buf != null) bufLocal.set(null); else buf = new char[2048]; } void close() { if (buf.length <= 1024 * 64) bufLocal.set(buf); } ThreadLocal 1. 通过 bufLocal 重用 char[] 2. 处理一个线程同时多个 SerializeWriter 的问题 1. close 时返还到 bufLocal 2. 控制大小避免 bufLocal 过大 1 void expandCapacity(int minimumCapacity) { int newCapacity = buf.length + (buf.length >> 1) + 1; if (newCapacity < minimumCapacity) newCapacity = minimumCapacity; buf = Arrays.copyOf(buf, newCapacity); } } // com.alibaba.fastjson.serializer.SerializeWriter 为了无法计算的价值
14. public class SymbolTable { final String[] symbols; final int indexMask; 1. 2. 3. 4. 用于保存常用的 Name ,减少对象创建 相同 hash 分桶的 symbol 只保留一份 在 char[] 直接和已存的 symbol 比较 预设必须存在的 symbol public SymbolTable(int tableSize) { this.indexMask = tableSize - 1; this.symbols = new String[tableSize]; this.addSymbol("$ref", 0, 4, "$ref".hashCode()); this.addSymbol("@type", 0, 5, ”@type".hashCode()); } public String addSymbol(char[] buffer, int offset, int len, int hash) { String symbol = symbols[hash & indexMask]; if (symbol != null) { if (hash == symbol.hashCode() && len == symbol.length()) { for (int i = 0; i < len; i++) if (buffer[offset + i] != symbol.charAt(i)) return new String(buffer, offset, len); } return symbol; } return symbols[hash & indexMask] = new String(buffer, offset, len).intern(); } } // com.alibaba.fastjson.parser.SymbolTable SymbolTable 2 为了无法计算的价值
15. public class IdentityHashMap<K, V> { final Entry<K, V>[] buckets final int indexMask; 1. 基于 System.identityHashCode 2. 不支持 resize 避免并发导致死循环 3. 特别处理避免锁缓存丢失不影响正确性 Identity HashMap public IdentityHashMap (int tableSize){ this.indexMask = tableSize - 1; this.symbols = new Entry[tableSize]; } public int int for boolean put(K key, V value) { hash = System.identityHashCode(key); bucket = hash & indexMask; (Entry e = buckets[bucket]; e != null; e = entry.next) { if (key == e.key) { e.value = value; return true; } 3 } Entry<K, V> e = new Entry<K, V>(key, value, hash, buckets[bucket]); buckets[bucket] = e; // 并发是处理时会可能导致缓存丢失,但不影响正确性 return false; } } // com.alibaba.fastjson.util.IdentityHashMap 为了无法计算的价值
16. public class JavaBeanInfo { final Class clazz; final Constructor defaultConstructor; final FieldInfo[] fields; } 1. getField/getMethod 的开销远大于反射调用 2. 缓存 Class 避免 ClassLoader.findClass 开销 public class FieldInfo { 3. 缓存 Constructor/Method/Field 减低反射开销 public final String name; 4. 缓存 Annotation 等信息减少访问元数据 API public final Method method; public final Field field; public final Class declaringClass public final JSONField fieldAnnotation; public final JSONField methodAnnotation; public Object get(Object object) { return method != null ? method.invoke(object) : field.get(object); } 降低 Reflect 开销 4 } // com.alibaba.fastjson.util.JavaBeanInfo // com.alibaba.fastjson.util.FieldInfo 为了无法计算的价值
17. // com.alibaba.fastjson.util.IOUtils#stringSize(long) static int stringSize(long x) { long p = 10; for (int i = 1; i < 19; i++) { if (x < p) return i; p = 10 * p; } 1. int/long 的序列化都采用相同的算法 return 19; 2. 比 toString + writeString 相比减少对象创建 } 3. float/double 是基于 toString+WriteString 实现较慢 // com.alibaba.fastjson.util.IOUtils#getChars(long, int, char[]) // com.alibaba.fastjson.util.IOUtils#getChars(int, int, char[]) static void getChars(long i, int index, char[] buf) { ... ... } int/long 序列化优化 5 // 在序列化这样使用 int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); char[] chars = new char[size]; getChars(i, size, chars); 为了无法计算的价值
18. // // // // com.alibaba.fastjson.parser.JSONScanner#scanInt com.alibaba.fastjson.parser.JSONScanner#scanLong com.alibaba.fastjson.parser.JSONScanner#scanFieldInt com.alibaba.fastjson.parser.JSONScanner#scanFieldLong int value; if (ch >= ‘0’ && ch <= ‘9’) { value = ch - ‘0’; for (;;) { ch = charAt(offset++); if (ch >= ‘0’ && ch<= ‘9’) { value = value * 10 + (ch - ‘0’); } else if (ch == ‘.’) { // error handle 1. 参考自 Integer.parseInt } else { 2. Integer.parseInt 不支持输入为 char[] break; 3. 针对 10 进制优化 } } } int->char[] Parse 优化 6 为了无法计算的价值
19. int intVal = ch - ‘0’, power = 1; for (;;) { ch = charAt(bp + (offset++)); if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (ch - ‘0’); continue; 1. Float.parseFloat 输入需要 String } else break; 2. Float.parseFloat 和 Double.parseDouble 很慢 } 3. 先当 int 处理,最后再除小数位得到 float if (ch == ‘.’) { 4. 在 android 某些机型上能得到数十倍的性能提升 ch = charAt(bp + (offset++)); 5. VR 相关的场景有大量的 float if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (chLocal - ‘0’); for (power = 10; ;power *= 10) { ch = charAt(bp + (offset++)); if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (ch - ‘0’); continue; } else break; } } } value = ((float) intVal) / power; if (negative) value = -value; float/double Parse 优化 7 为了无法计算的价值
20. package sun.nio; public class CharsetEncoder { ByteBuffer encode(CharBuffer in); } UTF8Encode 优化 package com.alibaba.fastjson; public class IOUtils { static int encodeUTF8(char[] chars, int offset, int len, byte[] bytes); } 1. 2. 3. 4. 5. 参考自 sun.nio.cs.ArrayEncoder 英文场景能快 25% 中文场景能快 100% 数组直接操作比 CharBuffer 速度快 sun.nio.cs.ArrayDecoder 在 Java8 英文场景性能不如 new String(K)byte, charset) 8 为了无法计算的价值
21. // 反序列化实现 public class ASMDeserializerFactory { public ObjectDeserializer createJavaBeanDeserializer() { ClassWriter cw = new ClassWriter(); MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "deserialze”); … … mw.visitVarInsn(ALOAD, context.var(“lexer”)); mw.visitVarInsn(BIPUSH, seperator); mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase , "scanString", "(C)Ljava/lang/String;"); ... ... byte[] code = cw.toByteArray(); Class deserClass = classLoader.defineClassPublic(code); return deserClass.newInstance(); } } // com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory // com.alibaba.fastjson.serializer.ASMSerializerFactory // 序列化实现 1. 内置一个 asm 实现 public class ASMSerializerFactory { 2. 基于 ObjectWeb ASM 3.3.1 裁剪 } 3. 不到 2000 行 4. 无依赖,体积小 5. 无反射开销 6. 产生式编程能减少分支判断 ASM 动态字 节码优化 9 为了无法计算的价值
22. 小方法内联 public class Model { public final int value; public Model(int value) { this.value = value; } } public class Model { private int value; public Model(int value) { this.value = value; } public int getValue() { return value; } } 1. 2. 3. 4. 5. 6. Android 下方法调用的开销较大 Android 下有方法数量 65535 的限制 无论 Android Dalvik 或者 HotSpot 方法调用都有开销 小方法手动内联是一个优化技巧,提升性能,减少体积 public final Field 代替只读 getter public Field 代替可读写的 getter & setter 10 为了无法计算的价值
23. {”id”:1001,”name”:”wenshao”} public class Person { public int id; public String name; } 1. 2. 3. 4. 5. 6. 7. 8. 9. Name 占据 JSON 字符串相当大的一部分 通过匹配而不是把 Name 读取出来性能大幅提升 读写按照相同的顺序匹配成功率会更高 缺失部分字段不会影响匹配 不符合顺序走普通模式 快速匹配算法合适使用 ASM 动态字节码实现 Name 匹配可以用 SIMD 指令优化 这个算法是性能超越 Jackson 的关键 快速匹配算法不适合用于 Android Person p = new Persson(); // 先挨个 Name 做匹配 if (matchField(“id”)) p.id = readInt(); if (matchField(“name”)) p.name = readString(); // 剩下不匹配的走常规模式 String name = readName(); FieldDeserializer fieldDeser = fndFieldDeserializer(name); if (fieldDeser != null) fieldDeser.readValue(); // charArrayCompare 是 matchField 的关键实现 static boolean charArrayCompare(String src, int offset, char[] dest) { for (int i = 0; i < destLen; ++i) { if (dest[i] != src.charAt(offset + i)) return false; return true; } } 快速匹配算法 11 为了无法计算的价值
24. {”id”:1001,”name”:”wenshao”} 1. 2. 3. 4. 5. 6. 快速排序需要动态字节码生成不能用于 Android 算法来自 dsl-platform 和 jsoniter 假设一个对象内不会存在相同 hash 值的 Name 避免了 Name 对象的创建,性能非常好 Android 版本使用 fnv_hash 匹配算法和标准不同 采用 fnv_hash_64 而不是 fnv_hash_32 不同于 jsoniter public class Person { public int id; public String name; } long readNameHash() { long hash = 0x811c9dc5; for (; i < text.length; ++p) { char ch = text.charAt(p); if (ch == '"') break; hash ^= ch; hash *= 0x1000193; } return hash; } long nameHash = readNameHash(); FieldDeserializer fieldDeser = fndFieldDeserializer(nameHash); if (fieldDeser != null) { fieldDeser.readValue(); } // 以上为伪码,真实实现会复杂很多 FNVHash 匹配算法 12 为了无法计算的价值
25. // 文档 https://github.com/alibaba/fastjson/wiki/BeanToArray_cn class Company { public int code; public List<Department> departments = new ArrayList<Department>(); } @JSONType(serialzeFeatures = SerializerFeature.BeanToArray , parseFeatures = Feature.SupportArrayToBean) class Department { public int id; public Stirng name; public Department() {} public Department(int id, String name) { this.id = id; 1. 开启 BeanToArray 之后,输出是 JSONArray 结构 this.name = name; 2. 局部开启 BeanToArray 能兼顾性能和可维护性的平衡 } 3. BeanToArray 模式性能超越大多数二进制协议 } BeanToArray 性能超强模式 13 Company cmpy = new Company(); cmpy.code = 100; cmpy.departments.add(new Department(1001, "Sales")); cmpy.departments.add(new Department(1002, "Financial")); // {"code":10,"departments":[[1001,"Sales"],[1002,"Financial"]]} String text = JSON.toJSONString(commpany); 为了无法计算的价值
26. public class EnumDeserializer { 1. 构建一个排好序的 hashcode 数组 protected final Enum[] enums; 2. 构建根据 hashCode 排序的 enums protected final long[] hashCodes; public EnumDeserializer (Class enumClass) { this.enums = (Enum[]) enumClass.getEnumConstants(); this.hashCodes = new long[enums.length]; for (int i = 0; i < enums.length; ++i) hashCodes[i] = fnv_hash_64(enums[i].name); Arrays.sort(hashCodes); Arrays.sort(enums, (a, b) -> { long x = fnv_hash_64(a.name()), y = fnv_hash_64(b.name()); return (x < y) ? -1 : ((x == y) ? 0 : 1) }); } public Enum getEnumByHashCode(long hashCode) { int index = Arrays.binarySearch(this.hashCodes, hashCode); if (index < 0) return null; 1. 读取 name 的 HashCode return enums[index]; 2. 通过 HashCode 查找 Enum } Enum scanEnum() { long hashCoce = lexer.scanHashCode(); return getEnumByHashCode(hashCode); } } Enum Parse 优化 14 为了无法计算的价值
27. // 方法一 String text = …; char[] chars = text.toCharArray(); // 这里会导致一次内存分配和拷贝,速度较慢 for (int i = 0; i < chars.length; ++i) { char ch = chars[i]; } // 方法二 for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); // Android 下每次调用 length() 方法会有开销 } // 方法三 for (int i = 0, len = text.length(); i < len; ++i) { char ch = text.charAt(i); } 字符串 遍历优化 15 1. Android 下 Dalvik 小方法调用不会内联有开销 2. 方法三是最优解,兼顾 HotSpot 和 Dalvik 为了无法计算的价值
28. public enum Feature { UseBigDecimal, SortFeidFastMatch, IgnoreNotMatch; public final int mask; private Feature() { mask = (1 << ordinal()); } } 1. 2. 3. 4. 5. BitFlags 使用 int 的每一个 bit 标识一个选项 int 只有 32 个 bit 所以只有 32 个选项 通过 & mask != 0 查看是否已设置选项 通过 |= 和 &= 配置选项 ASM 动态代码生成时会根据常用组合做优化 int features = 0; features |= Feature.UseBigDecimal.mask; features |= Feature.SortFeidFastMatch.mask; features |= Feature.IgnoreNotMatch.mask; public class JSONLexer { private int features; public boolean isEnabled(Feature feature) { return (features & feature.mask) != 0; } public void config(Feature feature, boolean state) { if (state) features |= feature.mask; else features &= ~feature.mask; } } 16 为了无法计算的价值
29. 查看 JVM Intrinsic 的代码路径 src/share/vm/classfile/vmSymbols.hpp class String { int compareTo(String); int indexOf(String); boolean equals(String); } 1. 2. 3. 4. JVM Intrinsic JVM 提供的 Instrinsic 很少 通常只是 System.arrayCopy 有用 查看是否 Instrinsic 注意参数类型匹配 在阿里关键方法可以提需求给 JVM 团队 class System { int identityHashCode(Object); long currentTimeMillis(); long nanoTime(); void arrayCopy(Object, int, Object, int, int); } 17 class Arrays { void copyOf(...); void copyOfRange(...); boolean equals(...); } http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/ vm/classfile/vmSymbols.hpp 为了无法计算的价值
30. class JSONScanner { public Calendar calendar; public boolean scanISO8601DateIfMatch() { char y0 = charAt(bp); char y1 = charAt(bp + 1); 1. SimpleDateFormat 线程不安全 char y2 = charAt(bp + 2); 2. SimpleDateFormat 一次只能识别一种格式 char y3 = charAt(bp + 3); 3. 一次遍历支持大多数常用 DateFormat 的识别 char M0 = charAt(bp + 4); char M1 = charAt(bp + 5); char d0 = charAt(bp + 6); char d1 = charAt(bp + 7); if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) return false; char h0 = charAt(bp + 8); char h1 = charAt(bp + 9); char m0 = charAt(bp + 10); char m1 = charAt(bp + 11); char s0 = charAt(bp + 12); char s1 = charAt(bp + 13); DateParse 优化 18 setCalendar(y0, y1, y2, y3, M0, M1, d0, d1); ... ... } } // com.alibaba.fastjson.parser.JSONScanner 为了无法计算的价值
31. No. 2 FASTJSON 优化技术总结 算法 GC 友 好 用好 Hash 算法 减少内存分配 用好简单类型 避免 算法 GC 锁 CP 友好 U友 为特定场景优化的算法 好 连续操作, CacheLine 对齐 批量操作, SIMD 优化 用好数组 CPU 友好 锁的开销会较大 避免锁 为了无法计算的价值
32. 1/3 FASTJSON 相关技术和功能点 1 2 3 TypeReference 用以支持泛型,避免编译擦除 JSONPath 支持求值、修改、统计 可以当做 OQL 使用 和 JSON 解析公用基础设施,保证性能 自定序列化 Filter 各种 SerializerFilter 4 5 6 自定义反序列化 用于支持 MapBean 的 ExtraProcessable 避免 SubString 引用问题 避免 Android 2/34/5 下 subString 引用问题 WriteClassName 自带类型信息 曾有安全漏洞 SeeAlso 为了无法计算的价值
33. Type Reference class Model {} // 例一 List<Model> models = JSON.parseObject(“[{},{},{}]” , new TypeReference<List<Model>>(){}); // 例二 Type type = new TypeRefrence<List<Model>>(){}.getType(); List<Model> models = JSON.parseObject(“[{},{},{}]”, type); // 例三 框架支持 class Response<T> { public T data; } 1. 通过内嵌声明一个 TypeReference 派生类获得类型 2. 避免了编译擦除泛型信息问题 3. 例二中的 type 单例化处理性能更好 1 public static <T> Response<T> parseRepsonse(String json, Type type) { return JSON.parseObject(json, new TypeReference<Response<T>>() {}); } 为了无法计算的价值
34. public class Entity { public Integer id; public String name; public Object value; public Entity() {} public Entity(Integer id, Object value) { this.id = id; this.value = value; } public Entity(String name) { this.name = name; } } Entity entity = new Entity(123, new Object()); assertSame(entity.value, JSONPath.eval(entity, "$.value")); assertTrue(JSONPath.contains(entity, "$.value")); assertTrue(JSONPath.containsValue(entity, "$.id", 123)); assertTrue(JSONPath.containsValue(entity, "$.value", entity.value)); assertEquals(2, JSONPath.size(entity, "$")); assertEquals(0, JSONPath.size(new Object[0], "$")); 1. 可以通过 JSONPath 求值、修改、统计、判断是否存 在 2. 可将 JSONPath 当做 OQL 使用 3. 和 JSON 解析共用基础设施,性能有保证 https://github.com/alibaba/fastjson/wiki/JSONPath JSONPath 2 为了无法计算的价值
35. // 根据 PropertyName 和 PropertyValue 来判断是否序列化 public interface PropertyPreFilter extends SerializeFilter { boolean apply(JSONSerializer serializer, Object obj, String name); } // 和 PropertyFilter 不同只根据 object 和 name 进行判断,在调用 getter 之前 // 这样避免了 getter 调用可能存在的异常 public interface PropertyPreFilter extends SerializeFilter { boolean apply(JSONSerializer serializer, Object obj, String name); } // 序列化时修改 Key public interface NameFilter extends SerializeFilter { String process(Object obj, String propertyName, Object propertyValue); } // 序列化是修改 Value public interface ValueFilter extends SerializeFilter { Object process(Object obj, String propertyName, Object propertyValue); } // 和 ValueFilter 类似,只是多了 BeanContext 参数可用。 public interface ContextValueFilter extends SerializeFilter { Object process(BeanContext ctx, Object obj, String name, Object val); } // 还有其他的 SerializeFilter LabelFilter SimplePropertyPreFilter // 注册在 Class 级别 https://github.com/alibaba/fastjson/wiki/Class_Level_SerializeFilter https://github.com/alibaba/fastjson/wiki/SerializeFilter 自定义 序列化 Filter 3 为了无法计算的价值
36. public class Model implements JSONSerializable, ExtraProcessable { protected Map<String, Object> attributes = new HashMap<String, Object>(); 自定义 反序列化 public Map<String, Object> getAttributes() { return attributes;} public Object getAttribute(String name) { return attributes.get(name); } public void write(JSONSerializer serializer , Object fieldName , Type fieldType , int features) throws IOException { serializer.write(attributes); // 定制序列化 } 4 public void processExtra(String key, Object value) { attributes.put(key, value); // 定制反序列化 } } 1. ExtraProcessable 用于不匹配类型的序列化,可用于使用 MapBean 的框架 2. 类似功能的还有 PropertyProcessable https://github.com/alibaba/fastjson/wiki/PropertyProcessable_cn https://github.com/alibaba/fastjson/wiki/ExtraProcessable 为了无法计算的价值
37. JDK 1.4/5/6 & Android 2/3/4/5 subString 返回的字符串会持有原字符串 char[] 引用 static boolean V6; // android 6 static { int version = -1; try { Class<?> clazz = Class.forName("android.os.Build$VERSION"); Field field = clazz.getField("SDK_INT"); version = field.getInt(null); } catch (Exception e) { // skip } V6 = version >= 23; } 避免 SubString 引用问题 5 // subString 处理 if (V6) { strVal = text.substring(startIndex, endIndex); } else { int chars_len = endIndex - startIndex; char[] chars = sub_chars(bp + offset, chars_len); strVal = new String(chars, 0, chars_len); } 为了无法计算的价值
38. public class Model { public int id; public String name; public Entity() {} public Entity(int id, String this.id = id; this.name = name; } } 1. 这个功能在 1.2.24 之前有安全漏洞 2. 1.2.25 之后会有安全保护 3. 自带类型信息使得 json 相当于 Java 的序列化 Write ClassName name) { Model model = new Model(3, "wenshao"); String text = JSON.toJSONString(model, SerializerFeature.WriteClassName); // 如下是 toJSONString 返回的 text , @type 是类型信息 // {"@type":"com.alibaba.json.demo.Model","id":3,"name":”wenshao"} 6 Model model2 = (Model) JSON.parse(text); assertEquals(model.id, model2.id); assertEquals(model.name, model2.name); 支持类似 JAXB 的 XmlSeeAlso 功能 https://github.com/alibaba/fastjson/wiki/JSONType_seeAlso_cn 安全升级公告 为了无法计算的价值
39.

首页 - Wiki
Copyright © 2011-2024 iteam. Current version is 2.137.1. UTC+08:00, 2024-11-22 14:21
浙ICP备14020137号-1 $访客地图$