使用FastJson进行驼峰下划线相互转换写法及误区

news2024/11/25 0:30:24

PropertyNamingStrategy

有四种序列化方式。
CamelCase策略,Java对象属性:personId,序列化后属性:persionId – 实际只改了首字母 大写变小写
PascalCase策略,Java对象属性:personId,序列化后属性:PersonId – 实际只改了首字母 小写变大写
SnakeCase策略,Java对象属性:personId,序列化后属性:person_id --大写字母前加下划线
KebabCase策略,Java对象属性:personId,序列化后属性:person-id -大写字母前加减号

public enum PropertyNamingStrategy {
                                    CamelCase, //驼峰
                                    PascalCase, //
                                    SnakeCase, //大写字母前加下划线 
                                    KebabCase;

    public String translate(String propertyName) {
        switch (this) {
            case SnakeCase: {
                StringBuilder buf = new StringBuilder();
                for (int i = 0; i < propertyName.length(); ++i) {
                    char ch = propertyName.charAt(i);
                    if (ch >= 'A' && ch <= 'Z') {
                        char ch_ucase = (char) (ch + 32);
                        if (i > 0) {
                            buf.append('_');
                        }
                        buf.append(ch_ucase);
                    } else {
                        buf.append(ch);
                    }
                }
                return buf.toString();
            }
            case KebabCase: {
                StringBuilder buf = new StringBuilder();
                for (int i = 0; i < propertyName.length(); ++i) {
                    char ch = propertyName.charAt(i);
                    if (ch >= 'A' && ch <= 'Z') {
                        char ch_ucase = (char) (ch + 32);
                        if (i > 0) {
                            buf.append('-');
                        }
                        buf.append(ch_ucase);
                    } else {
                        buf.append(ch);
                    }
                }
                return buf.toString();
            }
            case PascalCase: {
                char ch = propertyName.charAt(0);
                if (ch >= 'a' && ch <= 'z') {
                    char[] chars = propertyName.toCharArray();
                    chars[0] -= 32;
                    return new String(chars);
                }

                return propertyName;
            }
            case CamelCase: {
                char ch = propertyName.charAt(0);
                if (ch >= 'A' && ch <= 'Z') {
                    char[] chars = propertyName.toCharArray();
                    chars[0] += 32;
                    return new String(chars);
                }

                return propertyName;
            }
            default:
                return propertyName;
        }
    }

发挥作用的是translate方法

指定序列化格式

了解了PropertyNamingStrategy后,看其是怎么发挥作用的,
阅读源码发现在buildBeanInfo时(注意是将bean转为json时构建json信息时,如果是map,JSONObject不会有这个转换)

    if(propertyNamingStrategy != null && !fieldAnnotationAndNameExists){
                    propertyName = propertyNamingStrategy.translate(propertyName);
                }

这里分别调用PropertyNamingStrategy对应的方法处理

常见误区
那么也就是说通过PropertyNamingStrategy的方式设置输出格式,只对javaBean有效,并且,至于转换结果,需要根据PropertyNamingStrategy#translate方法的内容具体分析
如果javaBean中的字段是用下划线间隔的,那么指定CamelCase进行序列化,也是无法转成驼峰的!
例如

        Student student = new Student();
        student.setTest_name("test");
        SerializeConfig serializeConfig = new SerializeConfig();
        serializeConfig.setPropertyNamingStrategy(PropertyNamingStrategy.CamelCase);
        System.out.println(JSON.toJSONString(student,serializeConfig));

输出{test_name":“test”},因为执行 PropertyNamingStrategy#translate的CamelCase,仅仅只是,判断如果首字母大写转成小写。并不能完成,下划线到驼峰的转换

 case CamelCase: {
                char ch = propertyName.charAt(0);
                if (ch >= 'A' && ch <= 'Z') {
                    char[] chars = propertyName.toCharArray();
                    chars[0] += 32;
                    return new String(chars);
                }

                return propertyName;
            }

指定反序列化格式

智能匹配功能

fastjson反序列化时,是能自动下划线转驼峰的。这点是很方便的。,在反序列化时无论采用那种形式都能匹配成功并设置值

        String str = "{'user_name':123}";
        User user = JSON.parseObject(str, User.class);
        System.out.println(user);

输出{userName=‘123’}

fastjson智能匹配处理过程

fastjson在进行反序列化的时候,对每一个json字段的key值解析时,会调用
com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#parseField
这个方法

在这里插入图片描述

以上面的例子为例,通过debug打个断点看一下解析user_id时的处理逻辑。
此时这个方法中的key为user_id,object为要反序列化的结果对象,这个例子中就是FastJsonTestMain.UserInfo

    public boolean parseField(DefaultJSONParser parser, String key, Object object, Type objectType,
                              Map<String, Object> fieldValues, int[] setFlags) {
        JSONLexer lexer = parser.lexer; // xxx
        //是否禁用智能匹配;
        final int disableFieldSmartMatchMask = Feature.DisableFieldSmartMatch.mask;
        final int initStringFieldAsEmpty = Feature.InitStringFieldAsEmpty.mask;
        FieldDeserializer fieldDeserializer;
        if (lexer.isEnabled(disableFieldSmartMatchMask) || (this.beanInfo.parserFeatures & disableFieldSmartMatchMask) != 0) {
            fieldDeserializer = getFieldDeserializer(key);
        } else if (lexer.isEnabled(initStringFieldAsEmpty) || (this.beanInfo.parserFeatures & initStringFieldAsEmpty) != 0) {
            fieldDeserializer = smartMatch(key);
        } else {
            //进行智能匹配
            fieldDeserializer = smartMatch(key, setFlags);
        }
    
    ***此处省略N多行***
    }

再看下核心的代码,智能匹配smartMatch

public FieldDeserializer smartMatch(String key, int[] setFlags) {
        if (key == null) {
            return null;
        }
        
        FieldDeserializer fieldDeserializer = getFieldDeserializer(key, setFlags);

        if (fieldDeserializer == null) {
            if (this.smartMatchHashArray == null) {
                long[] hashArray = new long[sortedFieldDeserializers.length];
                for (int i = 0; i < sortedFieldDeserializers.length; i++) {
                	//java字段的nameHashCode,源码见下方
                    hashArray[i] = sortedFieldDeserializers[i].fieldInfo.nameHashCode;
                }
                //获取出反序列化目标对象的字段名称hashcode值,并进行排序
                Arrays.sort(hashArray);
                this.smartMatchHashArray = hashArray;
            }

            // smartMatchHashArrayMapping
            long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
            //进行二分查找,判断是否找到
            int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
            if (pos < 0) {
                //原始字段没有匹配到,用fnv1a_64_extract处理一下再次匹配
                long smartKeyHash1 = TypeUtils.fnv1a_64_extract(key);
                pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash1);
            }

            boolean is = false;
            if (pos < 0 && (is = key.startsWith("is"))) {
                //上面的操作后仍然没有匹配到,把is去掉后再次进行匹配
                smartKeyHash = TypeUtils.fnv1a_64_extract(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, sortedFieldDeserializers[i].fieldInfo.nameHashCode);
                        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;
    }

通过上面的smartMatch方法可以看出,fastjson中之所以能做到下划线自动转驼峰,主要还是因为在进行字段对比时,使用了fnv1a_64_lower和fnv1a_64_extract方法进行了处理。
fnv1a_64_extract方法源码:

    public static long fnv1a_64_extract(String key) {
        long hashCode = fnv1a_64_magic_hashcode;
        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 *= fnv1a_64_magic_prime;
        }
        return hashCode;
    }

从源码可以看出,fnv1a_64_extract方法主要做了这个事:
去掉下划线、减号,并大写转小写
总结
fastjson中字段智能匹配的原理是在字段匹配时,使用了TypeUtils.fnv1a_64_lower方法对字段进行全体转小写处理。
之后再用TypeUtils.fnv1a_64_extract方法对json字段进行去掉"_“和”-"符号,再全体转小写处理。
如果上面的操作仍然没有匹配成功,会再进行一次去掉json字段中的is再次进行匹配。
如果上面的操作仍然没有匹配成功,会再进行一次去掉json字段中的is再次进行匹配。

关闭智能匹配的情况

智能匹配时默认开启的,需要手动关闭,看这个例子

 String str = "{'user_name':123}";
        ParserConfig parserConfig = new ParserConfig();
        parserConfig.propertyNamingStrategy =  PropertyNamingStrategy.SnakeCase;
        User user = JSON.parseObject(str, User.class, parserConfig,Feature.DisableFieldSmartMatch);
        System.out.println(user);

输出{userName=‘null’}
那么这种情况如何完成下划线到驼峰的转换
那么就需要使用parseConfig了

        String str = "{'user_name':123}";
        ParserConfig parserConfig = new ParserConfig();
        parserConfig.propertyNamingStrategy =  PropertyNamingStrategy.SnakeCase;
        User user = JSON.parseObject(str, User.class,parserConfig,Feature.DisableFieldSmartMatch);
        System.out.println(user);

那么此时PropertyNamingStrategy.SnakeCase又是如何发挥作用的?
断点PropertyNamingStrategy#translate方法
发现在构建JavaBeanDeserializer时

public JavaBeanDeserializer(ParserConfig config, Class<?> clazz, Type type){
        this(config //
                , JavaBeanInfo.build(clazz, type, config.propertyNamingStrategy, config.fieldBased, config.compatibleWithJavaBean, config.isJacksonCompatible())
        );
    }
  if (propertyNamingStrategy != null) {
                propertyName = propertyNamingStrategy.translate(propertyName);
            }

            add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal, serialzeFeatures, parserFeatures,
                    annotation, fieldAnnotation, null, genericInfo));

会根据配置对propertyName进行translate。转换成对应格式的属性名称

常见误区:
与序列化误区相同,如果是map,JSONObject不会有这个转换,并且转换结果需要参照translate方方法逻辑来看
值的注意的是,JSONObject的toJavaObject方法,智能匹配会生效。可以放心得进行下划线和驼峰得互相转换

        String str = "{'user_name':123}";
        JSONObject object = (JSONObject) JSON.parse(str);
        System.out.println(object);
        User user = object.toJavaObject(User.class);
        System.out.println(user);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/126380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

说透IO多路复用模型

在说IO多路复用模型之前&#xff0c;我们先来大致了解下Linux文件系统。在Linux系统中&#xff0c;不论是你的鼠标&#xff0c;键盘&#xff0c;还是打印机&#xff0c;甚至于连接到本机的socket client端&#xff0c;都是以文件描述符的形式存在于系统中&#xff0c;诸如此类&…

springboot项目打war包 部署到Tomcat

1、SpringBoot项目Pom文件修改 <!-- 打war包配置 --><packaging>war</packaging><!-- 打war包配置 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>…

英美TOP名校对IB的申请要求汇总

英美TOP名校对IB的申请要求汇总 英国大学剑桥大学 IB要求 40-42分&#xff08;满分45&#xff09;&#xff0c;HL要求为776分。 学校可能要求申请者的某些科目成绩为7&#xff0c;视不同专业和学院而定。 对任何要求数学的专业&#xff0c;申请者需选Analysis and Approaches&a…

Google SEM和谷歌SEO的区别

很多人对Google SEM和Google SEO概念很模糊。米贸搜整理如下。看图: Google SEM和SEO的关系 在上图中&#xff0c; 最上面的部分属于Google SEM&#xff0c;即Google Ads广告推广&#xff0c;是一种按效果付费的广告&#xff1b; 底层属于Google SEO&#xff0c;也就是Googl…

前端基础_配置IIS服务器

配置IIS服务器 在应用程序完全离线之前&#xff0c;还需要正确地提供清单文件。清单文件必须有扩展名.manifest和正确的mime-type。 如果使用Apache这样的通用Web服务器&#xff0c;需要找到在AppServ/Apache2.2/conf文件夹中的mine.types文件并向其添加“text/cache-manifes…

React学习02-React面向组件编程

React 开发者工具 推荐使用Chrome或Edge浏览器&#xff0c;安装React Developer Tools&#xff08;Facebook出品&#xff09;。 安装完成后&#xff0c;访问使用React编写的页面时&#xff0c;图标会高亮&#xff08;开发环境为红色有debug标识&#xff0c;生产环境为蓝色&…

如何高效阅读一篇论文

如何阅读一篇论文&#xff08;做好阅读笔记&#xff09;阅读步骤第一遍第二遍第三遍上哪里找论文paperswithcodeconnectedpaperslabml.ai 深度学习论文实现labml.ai 热门研究论文阅读步骤 第一遍 第一次通过的目的是大致了解论文。 阅读作者姓名、标题、摘要、简介、小节标题…

create first django

django-admin startproject first 1. 运行第一个django.py文件 python manage.py runserver 2. 建立第一个app python manage.py startapp first_app 修改settings.py&#xff0c;将first_app加入到下面中 然后修改views.py 然后修改urls.py配置导入view文件 前面是一个正则表达…

一文速学-Pandas处理时间序列数据-时间/日期操作详解

前言 关于Pandas处理时间序列数据我已经有写过两篇处理文章了&#xff1a; 一文速学-Pandas中DataFrame转换为时间格式数据与处理 一文速学-Pandas处理时间序列数据操作详解 日常处理一些数据和业务上需求&#xff0c;其实还是十分常用到时序数据的&#xff0c;一些处理方…

堆排序,建初始堆以及优先队列(priority_queue)

1.堆&#xff1a; 如果有一个关键码的集合K {k0&#xff0c;k1&#xff0c; k2&#xff0c;…&#xff0c;kn-1}&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中&#xff0c;并满足&#xff1a;Ki < K2i1 且 Ki<K2i2 &#xff0c;则称为小堆…

Docker部署jenkins配置公私钥拉取代码

容器内配置公私钥 先进入部署Jenkinns中的容器&#xff0c;在docker容器内生成公私钥 ssh-keygen -t rsajenkins 配置私钥信息 在Dashbord->凭据->系统->全局凭据中新增一个凭据 将公钥配置在gitlab 正常这么配制就可以了&#xff0c;但是在jenkins上发现使用ssh…

如何快速掌握代币经济学

如何研究加密世界里的Token? 先看一组数据&#xff1a;截至2022年&#xff0c;市面上大约有6000种加密货币(或者更多&#xff09;。这对投资者来说当然是一个很大的机会。然而&#xff0c;在2021年&#xff0c;投资者在Crypto项目遇到欺诈&#xff0c;损失的金额120亿美元。因…

2022年河北沃克金属制品有限公司助力河北石家庄电子商务资源对接暨电商直播选品大会圆满落幕!

会议主题&#xff1a;聚合电商直播优势资源 赋能产业发展消费增长 主题活动&#xff1a;2022河北•石家庄电子商务资源对接暨电商直播选品大会 承办日期&#xff1a;2022年12月26日至2022年12月27日 主办单位&#xff1a;石家庄市商务局 指导单位&#xff1a;河北省商务厅 …

基于K8s的DevOps平台实践(一)

文章目录前言1. DevOps介绍&#x1f351; 瀑布式流程&#x1f351; 敏捷开发&#x1f351; DevOps2. Jenkins初体验&#x1f351; K8s环境中部署jenkins&#x1f351; 安装汉化插件3. Jenkins基本使用演示&#x1f351; 演示目标&#x1f351; 演示准备&#x1f351; 演示过程4…

04贪心算法

文章目录背包问题活动安排问题最优装载问题删数问题最优服务次序贪心算法 在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;他所做出的是在某种意义上的局部最优解。 过程&#xff1a; 建立数学模型来描述问…

奖励视频 — Verasity 的最新专利意味着什么?

目录 Verasity 的最新专利涵盖哪些内容&#xff1f; 美国专利审批流程——拒绝和批准 主张我们的专利并寻求许可费 这对 Verasity 意味着什么&#xff1f; 近日&#xff0c;我们宣布Verasity 已在全球最大的广告和媒体市场美国获得奖励视频专利。 该专利及其全部内容可在此…

Vue--》超详细教程——vite脚手架的搭建与使用

目录 vite 创建 vite 项目 目录文件的构成 vite项目的运行流程 开发者工具安装 vite vue官方提供了两种快速创建工程化的SPA项目的方式&#xff0c;一种是基于 vue-cli 创建的SPA项目&#xff0c;另一种就是基于 vite 创建的SPA项目。两者的区别如下&#xff1a; 说明v…

如何在电脑录屏?win10录屏快捷键ctrl+alt+

日常使用的电脑有很多功能未被大家发现&#xff0c;比如可以录制屏幕视频&#xff1b;那如何在电脑录屏&#xff1f;win10电脑录屏有没有什么快捷键可以快速录制&#xff1f;下面就一起和小编来看看win10录屏快捷键是如何在电脑录屏的&#xff0c;有需要的朋友可以去试试看。 一…

观察者模式Observer

1.意图&#xff1a;定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 2.结构 Subject&#xff08;目标&#xff09;知道它的观察者&#xff0c;可以有任意多个观察者观察同一个目标&#xff1…

Flutter GetX系列教程---GetxController

安装 将 GetX 添加到你的 pubspec.yaml 文件中 dependencies:get: ^4.6.5在需要用到的文件中导入&#xff0c;它将被使用。 import package:get/get.dart;GetxController介绍 在实际的项目开发过程中&#xff0c;我们不可能把UI代码、业务逻辑都放在一起处理&#xff0c;这…