一. 前言
在开发过程中经常遇到json解析和生成的问题,所以用自己也一直用fastjson来实现这个功能。
但是,最近遇到一个问题:
json字符串里面的数据很多都是"_"下划线的比如,op_id。
而在java里面,很多都是驼峰的写法,如opId
那这两种可以匹配然后解析吗?
二. http请求的解决方法
http请求有个@JsonProperty的注解,但是这个注解,fastjson不识别。
三. 智能匹配
fastjson提供了智能匹配的规则,下面写法会自动映射
op_id->opid->ipId
也就是说就算json字符串是'op_id',那java变量也可以用opid或者opId,然后也可以获取相应的数据。
如下:
publicclassRunme{ staticintONE_DAY_SECONDS=24*60*60*1000; publicstaticvoidmain(String[]args){ Stringjson="{\"op-id\":1000}"; Momo=JSON.parSEObject(json,Mo.class); System.out.println(mo.getOpId()); } publicstaticclassMo{ privateStringopId; publicStringgetOpId(){ returnopId; } publicvoidsetOpId(StringopId){ this.opId=opId; } } }
四. 原理分析
那fastjson是怎么做到的呢?
看了下源代码
https://github.com/alibaba/fastjson
发现它的逻辑如下:
文件:src/main/java/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java
方法: smartMatch(String key,int[] setFlags)
publicFieldDeserializersmartMatch(Stringkey,int[]setFlags){ if(key==null){ returnnull; } FieldDeserializerfieldDeserializer=getFieldDeserializer(key,setFlags); if(fieldDeserializer==null){ longsmartKeyHash=TypeUtils.fnv1a_64_lower(key); if(this.smartMatchHashArray==null){ long[]hashArray=newlong[sortedFieldDeserializers.length]; for(inti=0;i<sortedFieldDeserializers.length;i++){ hashArray[i]=TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name); } Arrays.sort(hashArray); this.smartMatchHashArray=hashArray; } //smartMatchHashArrayMapping intpos=Arrays.binarySearch(smartMatchHashArray,smartKeyHash); booleanis=false; if(pos<0&&(is=key.startsWith("is"))){ smartKeyHash=TypeUtils.fnv1a_64_lower(key.substring(2)); pos=Arrays.binarySearch(smartMatchHashArray,smartKeyHash); } if(pos>=0){ if(smartMatchHashArrayMapping==null){ short[]mapping=newshort[smartMatchHashArray.length]; Arrays.fill(mapping,(short)-1); for(inti=0;i<sortedFieldDeserializers.length;i++){ intp=Arrays.binarySearch(smartMatchHashArray,TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name)); if(p>=0){ mapping[p]=(short)i; } } smartMatchHashArrayMapping=mapping; } intdeserIndex=smartMatchHashArrayMapping[pos]; if(deserIndex!=-1){ if(!isSetFlag(deserIndex,setFlags)){ fieldDeserializer=sortedFieldDeserializers[deserIndex]; } } } if(fieldDeserializer!=null){ FieldInfofieldInfo=fieldDeserializer.fieldInfo; if((fieldInfo.parserFeatures&Feature.DisableFieldSmartMatch.mask)!=0){ returnnull; } ClassfieldClass=fieldInfo.fieldClass; if(is&&(fieldClass!=boolean.class&&fieldClass!=Boolean.class)){ fieldDeserializer=null; } } } returnfieldDeserializer; }
它里面有个重要的地方就是调用TypeUtils.fnv1a_64_lower(String)方法,分别计算传入的key(op_id)和定义的java成员变量opId,然后计算他们是否匹配。
如果匹配的话,就会覆盖。
所以,关键就在于TypeUtils.fnv1a_64_lower(String)方法实现,如下:
这个方法就是如果是'_'或者'-',那么就忽略,也就是如果是op_id,那么就会变成opid。
如果是大写,那么就转换成小写,也就是opId,就会变成opid。
所以op-id或者op_id,都可以匹配opId或者opid
publicstaticlongfnv1a_64_lower(Stringkey){ longhashCode=0xcbf29ce484222325L; for(inti=0;i<key.length();++i){ charch=key.charAt(i); if(ch=='_'||ch=='-'){ continue; } if(ch>='A'&&ch<='Z'){ ch=(char)(ch+32); } hashCode^=ch; hashCode*=0x100000001b3L; } returnhashCode; }