fastjson那些事

2020-02-23 288浏览

  • 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 bufLocal = new ThreadLocal(); 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 { final Entry[] 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 e = new Entry(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 departments = new ArrayList(); } @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 models = JSON.parseObject(“[{},{},{}]” , new TypeReference>(){}); // 例二 Type type = new TypeRefrence>(){}.getType(); List models = JSON.parseObject(“[{},{},{}]”, type); // 例三 框架支持 class Response { public T data; } 1. 通过内嵌声明一个 TypeReference 派生类获得类型 2. 避免了编译擦除泛型信息问题 3. 例二中的 type 单例化处理性能更好 1 public static Response parseRepsonse(String json, Type type) { return JSON.parseObject(json, new TypeReference>() {}); } 为了无法计算的价值
  • 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 attributes = new HashMap(); 自定义 反序列化 public Map 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.