关于json传输的过程中字段不对应的问题

2024-01-01 10:58

本文主要是介绍关于json传输的过程中字段不对应的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

https://github.com/singgel?tab=repositories 

前情摘要

先来一点故事补充,话说小明的领导给小明安排了一个任务,很简单就是调用别人的API,我们作为Client接收数据并进行相应的处理,领导说由于各种原因,目前不知道对方接口的返回数据格式,所以你先做数据解析吧,先写XML格式的,于是小明开始着手工作了,经过编码,调试,并且领导也review通过了。但是,领导接到消息说数据格式好像是JSON格式的,小明只好重新开始工作了。解析XML格式的代码请点击访问以下链接:

解析XML格式代码

代码编写之JSONObject解决一切

小明一听解析JSON格式,那不是手到擒来么,以前经常这么干,如以下代码所示

JSONObject jsonObject = new JSONObject(assetJson);JSONArray jsonArray = jsonObject.getJSONArray("value");for(int i=0;i<jsonArray.length();i++) {JSONObject jsonObj = jsonArray.getJSONObject(i);if(jsonObj.has("AssetNumber") && !jsonObj.isNull("AssetNumber")) {asset.setAssetNumber(jsonObj.getLong("AssetNumber"));}

这段代码应该都能读懂,就是传进来一个Json字符串,然后用一个实体类接收,为了防止异常保证代码健壮性,很多人都会像小明一样加上jsonObject.has(key)和jsonObject.isNull(key)。这时候小明正在为自己的小聪明得意,但是殊不知,这个json对象中有60多个key,意思就是得为这个小聪明多写120行重复的代码。然后再仔细一看,对方的API返回的数据,字段都是超级多,很显然,这么多重复代码无论你是放在Service层处理还是直接裸在Controller里都不是很友好。然后你是不是以为我要提高代码的可重用性,哈哈,这样的代码如果按这个思路来的话是没有办法简化的。那怎么办,换一个思路咯。(有点小啰嗦,请见谅)

代码编写之Json反序列化

小明想了想,现在整体项目使用的是spring boot,spring boot里集成了restTemplate,客户端根据自己的需要可以重写这里关于restTemplate的就不在赘述了。看到一篇关于HttpMessageConverter的文章,希望在你重写restTemplate的时候能帮助你完成关于消息类型转换的工作。如下:

https://segmentfault.com/a/1190000012659486

长话短说,封装好自己的restTemplate。小明想着使用实体类直接去接收服务端传过来的Json数据,代码如下:

public ResponseEntity<JsonResultForMaterial> getMaterials() {return this.restTemplate.exchange( getNlyteServiceEndpoint()+ GetMaterialsURL, HttpMethod.GET,getDefaultEntity(), JsonResultForMaterial.class);}

json数据例子如下:

{"@odata.context": "/$metadata#Materials","value": [{"@odata.type": "#Nlyte.Model.StandardNetworkMaterial","MaterialID": 1},{"@odata.type": "#Nlyte.Model.StandardServerMaterial","MaterialID": 96},{"@odata.type": "#Nlyte.Model.BladeServerMaterial","MaterialID": 101}],"@odata.nextLink": "Materials?$skip=200"}

 

由于服务端传过来的json数据格式问题,小明写了如下实体类

import java.util.List;import com.fasterxml.jackson.annotation.JsonProperty;public class JsonResultForMaterial {@JsonProperty(value="@odata.context")private String odatacontext;private List<Material> value;@JsonProperty(value="@odata.nextLink")private String odatanextLink;public String getOdatacontext() {return odatacontext;}public void setOdatacontext(String odatacontext) {this.odatacontext = odatacontext;}public String getOdatanextLink() {return odatanextLink;}public void setOdatanextLink(String odatanextLink) {this.odatanextLink = odatanextLink;}public List<Material> getValue() {return value;}public void setValue(List<Material> value) {this.value = value;}}package com.vmware.wormhole.nlyteworker.model;import com.fasterxml.jackson.annotation.JsonProperty;public class Material {@JsonProperty(value = "@odata.type")private String odataType;@JsonProperty(value = "MaterialID")private int materialID;public String getOdataType() {return odataType;}public void setOdataType(String odataType) {this.odataType = odataType;}public int getMaterialID() {return materialID;}public void setMaterialID(int materialID) {this.materialID = materialID;}}
在Json反序列化过程中,key需要和你定义的实体类的属性对应,但是大家都知道,实体类属性的命名规范,关于首字母和特殊字符的问题是很严格的。以上json数据中出现了,这样的key如:
@odata.context 、MaterialID

这样是无法直接用实体类接收的。这时候需要用到@JsonProperty(),来修饰具体属性,这样就可以解决无法映射的问题。

这样我们就获得了一个json结果对象,里面有我们需要操作的属性。这里不用考虑,开篇的那个问题,比如key是否存在,key的值是否为空。然后代码可以改成如下:

public List<Asset> getAssetsFromNlyte(List<NlyteAsset> nlyteAssets) {List<Asset> assetsFromNlyte = new ArrayList<Asset>();Asset asset;for(NlyteAsset nlyteAsset:nlyteAssets) {asset = new Asset();asset.setAssetNumber(nlyteAsset.getAssetNumber());asset.setTag(nlyteAsset.getTag());assetsFromNlyte.add(asset);}return assetsFromNlyte;}

这里其实是和开篇的代码做的事情是一样的,不同的是,开篇的代码接收到的是一个json字符串,需要使用JSONObject和JSONArray等进行处理,还需要判断key等等,最后再进行数据的封装。这里的代码直接接收到一个list,然后直接进行遍历,封装数据。这里就解决了,要写很多has(key)和isNull(key)判断的问题。

 

@JsonProperty,@NotNull,@JsonIgnore的具体实例使用,和其中发现的一些问题。

场景分析一

小明做了一个web表单,用来填写并保存数据,后台写restful接口接收数据并保存。写完之后让老大review的时候,自信满满的小明,又收到了很多comment。小明看到了这些comment发现确实有不足之处,比如表单里的有些数据是必须不为空的,虽然在页面上加上了强校验(Js校验),但是后台接口是对外开放的restful接口,别人不走页面直接访问接口存储数据,这时候页面的校验就显得很尴尬了,小明又想这好办啊,直接拿接收到的参数进行非空判断不就行了么,其实也是可以的,但是小明在研究@JsonProperty的时候发现@NotNull正好解决这个问题。代码展示如下:

Student类

public class Student {@JsonProperty(value="real_name")private String name ;@NotNull(message="idcard is not null")private String idCard;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIdCard() {return idCard;}public void setIdCard(String idCard) {this.idCard = idCard;}}

StudentController.java(注:这里为了测试方便未按照标准的restApi书写,如需学习标准的restful接口风格请移步百度。请见谅)

public class Student {@JsonProperty(value="real_name")private String name ;@NotNull(message="idcard is not null")private String idCard;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIdCard() {return idCard;}public void setIdCard(String idCard) {this.idCard = idCard;}}

Postman测试如下:(条件是:正常输入real_name和idCard,返回结果正常)

Postman测试结果如下(条件:只写real_name,不填写idCard。报错)

注意接口书写时,用@RequestBody接收输入参数时,这时候也需要匹配你预先定义的@JsonProperty的值。参考real_name.并且,在参数前需要加上@Valid,你定义的@NotNull校验才会生效。

@JsonProperty(value="real_name")
private String name ;

场景分析二

idCard为用户的敏感信息,在接口返回数据中不能展示出来,以免用户敏感信息直接暴露在外。这时候,小明想到了另外一个注解,@JsonIgnore,在Student对象序列化为json数据的返回的时候,忽略该属性。代码及测试如下:

@JsonProperty(value="real_name")private String name ;@JsonIgnoreprivate String idCard;

Postman测试如下(条件:正常输入real_name和idCard,观察返回数据,只包含real_name)

 

场景分析三

在场景二中提到使用@JsonIgnore可以让接口在返回数据的时候不序列化一些属性。但是小明又想了,若场景一和场景二结合使用,及在用户输入表单保存数据的时候,某个字段不能为空,并且返回数据的时候又不能包含该属性,是不是可以使用组合注解@JsonIngore和@NotNull呢,代码和测试结果如下:

@JsonProperty(value="real_name")private String name ;@JsonIgnore@NotNull(message="idcard is not null")private String idCard;

Postman测试结果如下(条件:idCard及为上述特殊字段,结果报错)

 

经过尝试,小明想到了如下解决方案,代码及测试结果如下:

@JsonProperty(value="real_name")private String name ;@JsonProperty(access=Access.WRITE_ONLY)@NotNull(message="idcard is not null")private String idCard;

Postman测试结果如下(条件:用JsonProperty代替JsonIgnore)

 

成功实现需求。

场景分析四

由于小明公司秉承尽最大努力少使用第三方的资源的原因,项目中关于JsonObject相关的jar都是使用的org.json,并未使用阿里的fastjson,如果项目使用的是fastjson,再使用上述的注解就不起作用了,它有自己的一套注解来解决上述问题,如:@JSONField,具体可参考com.alibaba.fastjson.annotation包。感兴趣的可以查一下。如有问题欢迎交流和分享。

这篇关于关于json传输的过程中字段不对应的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/559040

相关文章

Django HTTPResponse响应体中返回openpyxl生成的文件过程

《DjangoHTTPResponse响应体中返回openpyxl生成的文件过程》Django返回文件流时需通过Content-Disposition头指定编码后的文件名,使用openpyxl的sa... 目录Django返回文件流时使用指定文件名Django HTTPResponse响应体中返回openp

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

批量导入txt数据到的redis过程

《批量导入txt数据到的redis过程》用户通过将Redis命令逐行写入txt文件,利用管道模式运行客户端,成功执行批量删除以Product*匹配的Key操作,提高了数据清理效率... 目录批量导入txt数据到Redisjs把redis命令按一条 一行写到txt中管道命令运行redis客户端成功了批量删除k

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Win10安装Maven与环境变量配置过程

《Win10安装Maven与环境变量配置过程》本文介绍Maven的安装与配置方法,涵盖下载、环境变量设置、本地仓库及镜像配置,指导如何在IDEA中正确配置Maven,适用于Java及其他语言项目的构建... 目录Maven 是什么?一、下载二、安装三、配置环境四、验证测试五、配置本地仓库六、配置国内镜像地址

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3

Python进行JSON和Excel文件转换处理指南

《Python进行JSON和Excel文件转换处理指南》在数据交换与系统集成中,JSON与Excel是两种极为常见的数据格式,本文将介绍如何使用Python实现将JSON转换为格式化的Excel文件,... 目录将 jsON 导入为格式化 Excel将 Excel 导出为结构化 JSON处理嵌套 JSON:

解决RocketMQ的幂等性问题

《解决RocketMQ的幂等性问题》重复消费因调用链路长、消息发送超时或消费者故障导致,通过生产者消息查询、Redis缓存及消费者唯一主键可以确保幂等性,避免重复处理,本文主要介绍了解决RocketM... 目录造成重复消费的原因解决方法生产者端消费者端代码实现造成重复消费的原因当系统的调用链路比较长的时

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统