Fastjson那些事
-
- FASTJSON那些事 阿里中间件 温绍锦 2017.07.26 为了无法计算的价值
-
2. FASTJSON发展历程 json-lib太慢 Jackson-1.x API不友好 Jackson-1.x LGPG协议 项目创建 2010.12 开源&发展 2011.1开源到alibabatech.org 2011.7开源到github.com/alibaba github.com/eishay/jvm-serializers 评测json/databind分类最快 2012年最受欢迎中国开源软件 2013年开源中国10大热门开源项目 2016年最受欢迎中国开源软件 2016年发布1.1.52.android针对android做性能优化 2017年maven中央仓库月下载超过15万 被广泛采用 2012~2017 安全漏洞 2017.1.27除夕确认漏洞开始修复 2017.3.15内部修复完毕对外公告 漏洞披露规范未造成严重影响 2011 2017.1 为了无法计算的价值
-
3. 协议类型 序列化 反序列化 json/dsl-platform 文本 526 806 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 Eishay性能对比 数据来源 https://github.com/eishay/jvm-serializers/wiki json/databind分类最快 为了无法计算的价值 基于数组性能极致 比流行的二进制协议快!
-
4. 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 支持 不支持 不支持 不支持 不支持 支持泛型 支持 支持 支持 支持循环引用 支持 不支持 不支持 不支持 为了无法计算的价值 Java JSON库同类产品对比
-
5. 526 650 796 1173 1058 1455 1164 2842 4667 4105 5046 19853 806 696 1052 719 1241 731 1866 4622 4403 5449 23279 71969 0 10000 20000 30000 40000 50000 60000 70000 80000 json/dsl-platform json-array/fastjson/databind msgpack/databind protobuf json/fastjson/databind thrift json/jackson/databind hessian json/gson/databind bson/jackson/databind java-built-in json/json-lib/databind 反序列化 序列化
-
6. 481 214 1580 232 205 0 200 400 600 800 1000 1200 1400 1600 1800 fastjson fastjson-android jackson gson moshi 大小(K) 最新版大小(K) 9916 3070 9506 2911 0 2000 4000 6000 8000 10000 12000 fastjson jackson gson moshi github star github fork
-
7. fastjson-1.1.60.android gson-2.8.1 内置org.json moshi-1.5.0 jackson-2.8.9 首次序列化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次 2034 2375 5039 2449 为了无法计算的价值 Android环境性能对比 • 测试环境-华为P10(低端手机环境fastjson会更有优势) • 数值为耗时,单位毫秒,越小越好 • 内置org.json在某些机型下性能会较好,但都不如fastjson • 首次序列化/反序列化在fastjson每次new ParserConfig/SerializeConfig,jackson每次new ObjectMapper,gson每次new Gson 在Android下绝对优势碾压其他JSON库,针对首次优化对首屏快速展示效果非常好
-
8. 0 2000 4000 6000 8000 10000 12000 14000 16000 fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9 首次序列化10000次 0 5000 10000 15000 20000 25000 fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9 首次反序列化10000次 0 1000 2000 3000 4000 5000 6000 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 序列化100000次 0 500 1000 1500 2000 2500 3000 3500 4000 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 反序列化100000次 0 500 1000 1500 2000 2500 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 TradeObjectParse 26k1000次 0 1000 2000 3000 4000 5000 6000 fastjson-1.1.60.android gson-2.8.1 内置org.json jackson-2.8.9 CartObjectParse-70k1000次
-
9. FASTJSON的优点 性能最好 第三方评测性能最好 Android 4/5/6/7下性能最好 活跃受欢迎 Github上同类产品Star最多 多年开源中国最受欢迎开源软件评比在前10 稳定 回归测试3869个 测试覆盖率86% 功能完备 支持泛型、JDK8 支持JSONPath 要想写什 么就写什 么吧! 为了无法计算的价值 开源在github上 https://github.com/alibaba/fastjson Maven仓库下载地址 http://repo1.maven.org/maven2/com/alibaba/fastjson/
-
10. 性能优化技术 1/3 使用ThreadLocal 1 2 3 SymbolTable IdentityHashMap 在SerializeWriter/JSONScanner中使用ThreadLocal保存一 个byte[]/char[],减少内存分配 一个特别优化过的HashMap,将常用的Name保存起来,不 用每次分配 能避免并发导致的死锁和正确性问题的IdentityHashMap 缓存Method/Field/Constructor 4 5 6 针对int/long序列化的优化 针对int/long反序列化的优化 在ParserConfig/SerializeConfig中保存 Class/Method/Field/Constructor,降低反射开销 针对int/long类型序列化做特别优化, 避免toString+WriteString减少对象分配 参考自Integer.parseInt Integer.parseInt不支持输入为char[],支持String类型参数 针对10进制优化 为了无法计算的价值
-
11. 性能优化技术 2/3 针对float/double反序列化的优化 7 8 9 encodeUTF8/decodeUTF8优化 使用asm动态生成serializer/deserializer Float/Doubble.parse方法很慢 VR场景有大量的float/double 参考sun.nio.cs.ArrayEncoder/ sun.nio.cs.ArrayDecoder 内置asm,基于objectweb asm 3.3改造,只保留必要部分, 不到2000行代码 小方法手动内联 10 11 12 快速匹配算法 fnv_hash匹配优化 Android方法调用开销大 Oracle HotSpot的内联效果也不够好 直接访问Field比访问getter/setter更好 Fastjson独创的提升json反序列化性能的算法 基于字节码动态生成实现 性能超越jackson的关键 来自json/dsl-platform的优化算法 用于android版本性能优化 为了无法计算的价值
-
12. 性能优化技术 3/3 BeanToArray超强性能模式 Enum Parse优化 字符串遍历优化 超越大多数二进制协议 兼顾性能和可维护性 避免Name对象创建 兼顾HotSpot和Android Dalvik的最优解法 BitFlags 16 17 18 JVM Instrinstic 日期类型Parse优化 一个Int表示32个选项 结合Enum的ordinal 利用JVM Intrinsic方法优化 SimpleDateFormat线程并不安全 SimpleDateFormat一次只能匹配一种格式 为了无法计算的价值 13 14 15
-
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); } 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 1.通过bufLocal重用char[] 2. 处理一个线程同时多个SerializeWriter的问题 1. close时返还到bufLocal 2. 控制大小避免bufLocal过大 1 ThreadLocal
-
14. public class SymbolTable { final String[] symbols; final int indexMask; 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 2 1. 用于保存常用的Name,减少对象创建 2. 相同hash分桶的symbol只保留一份 3. 在char[]直接和已存的symbol比较 4. 预设必须存在的symbol SymbolTable 为了无法计算的价值
-
15. public class IdentityHashMap<K, V> { final Entry<K, V>[] buckets final int indexMask; public IdentityHashMap (int tableSize){ this.indexMask = tableSize - 1; this.symbols = new Entry[tableSize]; } public boolean put(K key, V value) { int hash = System.identityHashCode(key); int bucket = hash & indexMask; for (Entry e = buckets[bucket]; e != null; e = entry.next) { if (key == e.key) { e.value = value; return true; } } Entry<K, V> e = new Entry<K, V>(key, value, hash, buckets[bucket]); buckets[bucket] = e; // 并发是处理时会可能导致缓存丢失,但不影响正确性 return false; } } // com.alibaba.fastjson.util.IdentityHashMap 3 1. 基于System.identityHashCode 2. 不支持resize避免并发导致死循环 3. 特别处理避免锁缓存丢失不影响正确性 为了无法计算的价值 Identity HashMap
-
16. public class JavaBeanInfo { final Class clazz; final Constructor defaultConstructor; final FieldInfo[] fields; } public class FieldInfo { public final String name; 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); } } // com.alibaba.fastjson.util.JavaBeanInfo // com.alibaba.fastjson.util.FieldInfo 1. getField/getMethod的开销远大于反射调用 2. 缓存Class避免ClassLoader.findClass开销 3. 缓存Constructor/Method/Field减低反射开销 4. 缓存Annotation等信息减少访问元数据API 为了无法计算的价值 4 降低 Reflect开销
-
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; } return 19; } // 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 size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); char[] chars = new char[size]; getChars(i, size, chars); 为了无法计算的价值 5 int/long 序列化优化 1. int/long的序列化都采用相同的算法 2. 比toString + writeString相比减少对象创建 3. float/double是基于toString+WriteString实现较慢
-
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 } else { break; } } } 为了无法计算的价值 int->char[] Parse优化 1. 参考自Integer.parseInt 2. Integer.parseInt不支持输入为char[] 3. 针对10进制优化 6
-
19. 为了无法计算的价值 int intVal = ch - ‘0’, power = 1; for (;;) { ch = charAt(bp + (offset++)); if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (ch - ‘0’); continue; } else break; } if (ch == ‘.’) { ch = charAt(bp + (offset++)); 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; 1. Float.parseFloat输入需要String 2. Float.parseFloat和Double.parseDouble很慢 3. 先当int处理,最后再除小数位得到float 4. 在android某些机型上能得到数十倍的性能提升 5. VR相关的场景有大量的float 7 float/double Parse优化
-
20. package sun.nio; public class CharsetEncoder { ByteBuffer encode(CharBuffer in); } package com.alibaba.fastjson; public class IOUtils { static int encodeUTF8(char[] chars, int offset, int len, byte[] bytes); } 8 1. 参考自sun.nio.cs.ArrayEncoder 2. 英文场景能快25% 3. 中文场景能快100% 4. 数组直接操作比CharBuffer速度快 5. sun.nio.cs.ArrayDecoder在Java8英文场景性能不如new String(byte, charset) UTF8Encode 优化 为了无法计算的价值
-
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 // 序列化实现 public class ASMSerializerFactory { } 9 1. 内置一个asm实现 2. 基于ObjectWeb ASM 3.3.1裁剪 3. 不到2000行 4. 无依赖,体积小 5. 无反射开销 6. 产生式编程能减少分支判断 为了无法计算的价值 ASM动态字 节码优化
-
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. Android下方法调用的开销较大 2. Android下有方法数量65535的限制 3. 无论Android Dalvik或者HotSpot方法调用都有开销 4. 小方法手动内联是一个优化技巧,提升性能,减少体积 5. public final Field代替只读getter 6. public Field代替可读写的getter & setter 为了无法计算的价值 10 小方法内联
-
23. {”id”:1001,”name”:”wenshao”} public class Person { public int id; public String name; } 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 快速匹配算法1. Name占据JSON字符串相当大的一部分 2. 通过匹配而不是把Name读取出来性能大幅提升 3. 读写按照相同的顺序匹配成功率会更高 4. 缺失部分字段不会影响匹配 5. 不符合顺序走普通模式 6. 快速匹配算法合适使用ASM动态字节码实现 7. Name匹配可以用SIMD指令优化 8. 这个算法是性能超越Jackson的关键 9. 快速匹配算法不适合用于Android
-
24. {”id”:1001,”name”:”wenshao”} 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 匹配算法1. 快速排序需要动态字节码生成不能用于Android 2. 算法来自dsl-platform和jsoniter 3. 假设一个对象内不会存在相同hash值的Name 4. 避免了Name对象的创建,性能非常好 5. Android版本使用fnv_hash匹配算法和标准不同 6. 采用fnv_hash_64而不是fnv_hash_32不同于jsoniter 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; this.name = name; } } 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); 13 BeanToArray 性能超强模式 1. 开启BeanToArray之后,输出是JSONArray结构 2. 局部开启BeanToArray能兼顾性能和可维护性的平衡 3. BeanToArray模式性能超越大多数二进制协议
-
26. public class EnumDeserializer { protected final Enum[] 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; return enums[index]; } Enum scanEnum() { long hashCoce = lexer.scanHashCode(); return getEnumByHashCode(hashCode); } } 为了无法计算的价值 14 Enum Parse 优化 1. 读取name的HashCode 2. 通过HashCode查找Enum 1. 构建一个排好序的hashcode数组 2. 构建根据hashCode排序的enums
-
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()); } } 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 BitFlags 1. 使用int的每一个bit标识一个选项 2. int只有32个bit所以只有32个选项 3. 通过 & mask != 0查看是否已设置选项 4. 通过 |= 和 &=配置选项 5. ASM动态代码生成时会根据常用组合做优化
-
29. 查看JVM Intrinsic的代码路径 src/share/vm/classfile/vmSymbols.hpp class String { int compareTo(String); int indexOf(String); boolean equals(String); } class System { int identityHashCode(Object); long currentTimeMillis(); long nanoTime(); void arrayCopy(Object, int, Object, int, int); } class Arrays { void copyOf(...); void copyOfRange(...); boolean equals(...); } http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/v m/classfile/vmSymbols.hpp 为了无法计算的价值 17 JVM Intrinsic 1. JVM提供的Instrinsic很少 2. 通常只是System.arrayCopy有用 3. 查看是否Instrinsic注意参数类型匹配 4. 在阿里关键方法可以提需求给JVM团队
-
30. class JSONScanner { public Calendar calendar; public boolean scanISO8601DateIfMatch() { char y0 = charAt(bp); char y1 = charAt(bp + 1); char y2 = charAt(bp + 2); char y3 = charAt(bp + 3); 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); setCalendar(y0, y1, y2, y3, M0, M1, d0, d1); ... ... } } // com.alibaba.fastjson.parser.JSONScanner 为了无法计算的价值 DateParse 优化 18 1. SimpleDateFormat线程不安全 2. SimpleDateFormat一次只能识别一种格式 3. 一次遍历支持大多数常用DateFormat的识别
-
31. FASTJSON优化技术总结 No. 2 GC友好 减少内存分配 用好简单类型 CPU友好 连续操作,CacheLine对齐 批量操作,SIMD优化 用好数组 算法 用好Hash算法 为特定场景优化的算法 避免锁 锁的开销会较大 为了无法计算的价值
-
32. FASTJSON相关技术和功能点 1/3 TypeReference 1 2 3 JSONPath 自定序列化Filter 用以支持泛型,避免编译擦除 支持求值、修改、统计 可以当做OQL使用 和JSON解析公用基础设施,保证性能 各种SerializerFilter 自定义反序列化 4 5 6 避免SubString引用问题 WriteClassName 用于支持MapBean的ExtraProcessable 避免Android 2/34/5下subString引用问题 自带类型信息 曾有安全漏洞 SeeAlso 为了无法计算的价值
-
33. 为了无法计算的价值 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; } public static <T> Response<T> parseRepsonse(String json, Type type) { return JSON.parseObject(json, new TypeReference<Response<T>>() {}); } Type Reference 1. 通过内嵌声明一个TypeReference派生类获得类型 2. 避免了编译擦除泛型信息问题 3. 例二中的type单例化处理性能更好 1
-
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], "$")); 为了无法计算的价值 JSONPath https://github.com/alibaba/fastjson/wiki/JSONPath 2 1. 可以通过JSONPath求值、修改、统计、判断是否存在 2. 可将JSONPath当做OQL使用 3. 和JSON解析共用基础设施,性能有保证
-
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 为了无法计算的价值 自定义 序列化Filter 3 https://github.com/alibaba/fastjson/wiki/SerializeFilter
-
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); // 定制序列化 } public void processExtra(String key, Object value) { attributes.put(key, value); // 定制反序列化 } } 自定义 反序列化 1. ExtraProcessable用于不匹配类型的序列化,可用于使用MapBean的框架 2. 类似功能的还有PropertyProcessable 4 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处理 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); } 为了无法计算的价值 避免 SubString 引用问题 5
-
38. 为了无法计算的价值 public class Model { public int id; public String name; public Entity() {} public Entity(int id, String name) { this.id = id; this.name = 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"} Model model2 = (Model) JSON.parse(text); assertEquals(model.id, model2.id); assertEquals(model.name, model2.name); Write ClassName 1. 这个功能在1.2.24之前有安全漏洞 2. 1.2.25之后会有安全保护 3. 自带类型信息使得json相当于Java的序列化 6 安全升级公告 https://github.com/alibaba/fastjson/wiki/security\_update\_20170315 支持类似JAXB的XmlSeeAlso功能 https://github.com/alibaba/fastjson/wiki/JSONType\_seeAlso\_cn