fastjson處理下划線和駝峰問題的方法和源碼分析


一. 前言
在開發過程中經常遇到json解析和生成的問題,所以用自己也一直用fastjson來實現這個功能。
但是,最近遇到一個問題:
json字符串里面的數據很多都是"_"下划線的比如,op_id。
而在java里面,很多都是駝峰的寫法,如opId
那這兩種可以匹配然后解析嗎?

二. http請求的解決方法
http請求有個@JsonProperty的注解,但是這個注解,fastjson不識別;不過fastjson支持JSONField注解,如下:
@JSONField(name="sta")
private String status;

三. 智能匹配
fastjson提供了智能匹配的規則,下面寫法會自動映射op_id->opid->ipId
也就是說就算json字符串是'op_id',那java變量也可以用opid或者opId,然后也可以獲取相應的數據。

如下:
public class Runme {
static int ONE_DAY_SECONDS = 24 * 60 * 60 * 1000;
public static void main(String[] args) {
String json = "{\"op-id\":1000}";
Mo mo = JSON.parseObject(json, Mo.class);
System.out.println(mo.getOpId());
}

public static class Mo {
private String opId;
public String getOpId() {
return opId;
}

public void setOpId(String opId) {
this.opId = opId;
}
}
}

四. 原理分析
那fastjson是怎么做到的呢?
看了下源代碼
https://github.com/alibaba/fastjson
發現它的邏輯如下:
文件:src/main/java/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java
方法: smartMatch(String key, int[] setFlags)
public FieldDeserializer smartMatch(String key, int[] setFlags) {
if (key == null) {
return null;
}
FieldDeserializer fieldDeserializer = getFieldDeserializer(key, setFlags);
if (fieldDeserializer == null) {
long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
if (this.smartMatchHashArray == null) {
long[] hashArray = new long[sortedFieldDeserializers.length];
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
hashArray[i] = TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name);
}
Arrays.sort(hashArray);
this.smartMatchHashArray = hashArray;
}
// smartMatchHashArrayMapping
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
boolean is = 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 = new short[smartMatchHashArray.length];
Arrays.fill(mapping, (short) -1);
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
int p = Arrays.binarySearch(smartMatchHashArray
, TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name));
if (p >= 0) {
mapping[p] = (short) i;
}
}
smartMatchHashArrayMapping = mapping;
}
int deserIndex = smartMatchHashArrayMapping[pos];
if (deserIndex != -1) {
if (!isSetFlag(deserIndex, setFlags)) {
fieldDeserializer = sortedFieldDeserializers[deserIndex];
}
}
}
if (fieldDeserializer != null) {
FieldInfo fieldInfo = fieldDeserializer.fieldInfo;
if ((fieldInfo.parserFeatures & Feature.DisableFieldSmartMatch.mask) != 0) {
return null;
}
Class fieldClass = fieldInfo.fieldClass;
if (is && (fieldClass != boolean.class && fieldClass != Boolean.class)) {
fieldDeserializer = null;
}
}
}
return fieldDeserializer;
}

它里面有個重要的地方就是調用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

public static long fnv1a_64_lower(String key){
long hashCode = 0xcbf29ce484222325L;
for(int i = 0; i < key.length(); ++i){
char ch = key.charAt(i);
if(ch == '_' || ch == '-'){
continue;
}
if(ch >= 'A' && ch <= 'Z'){
ch = (char) (ch + 32);
}
hashCode ^= ch;
hashCode *= 0x100000001b3L;
}
return hashCode;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM