关于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

相关文章

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

使用Python将JSON,XML和YAML数据写入Excel文件

《使用Python将JSON,XML和YAML数据写入Excel文件》JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体,本文将介绍如何... 目录如何使用python写入数据到Excel工作表用Python导入jsON数据到Excel工作表用

Pyserial设置缓冲区大小失败的问题解决

《Pyserial设置缓冲区大小失败的问题解决》本文主要介绍了Pyserial设置缓冲区大小失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录问题描述原因分析解决方案问题描述使用set_buffer_size()设置缓冲区大小后,buf

PyInstaller打包selenium-wire过程中常见问题和解决指南

《PyInstaller打包selenium-wire过程中常见问题和解决指南》常用的打包工具PyInstaller能将Python项目打包成单个可执行文件,但也会因为兼容性问题和路径管理而出现各种运... 目录前言1. 背景2. 可能遇到的问题概述3. PyInstaller 打包步骤及参数配置4. 依赖

resultMap如何处理复杂映射问题

《resultMap如何处理复杂映射问题》:本文主要介绍resultMap如何处理复杂映射问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录resultMap复杂映射问题Ⅰ 多对一查询:学生——老师Ⅱ 一对多查询:老师——学生总结resultMap复杂映射问题

java实现延迟/超时/定时问题

《java实现延迟/超时/定时问题》:本文主要介绍java实现延迟/超时/定时问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java实现延迟/超时/定时java 每间隔5秒执行一次,一共执行5次然后结束scheduleAtFixedRate 和 schedu

如何解决mmcv无法安装或安装之后报错问题

《如何解决mmcv无法安装或安装之后报错问题》:本文主要介绍如何解决mmcv无法安装或安装之后报错问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mmcv无法安装或安装之后报错问题1.当我们运行YOwww.chinasem.cnLO时遇到2.找到下图所示这里3.

浅谈配置MMCV环境,解决报错,版本不匹配问题

《浅谈配置MMCV环境,解决报错,版本不匹配问题》:本文主要介绍浅谈配置MMCV环境,解决报错,版本不匹配问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录配置MMCV环境,解决报错,版本不匹配错误示例正确示例总结配置MMCV环境,解决报错,版本不匹配在col

Vue3使用router,params传参为空问题

《Vue3使用router,params传参为空问题》:本文主要介绍Vue3使用router,params传参为空问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录vue3使用China编程router,params传参为空1.使用query方式传参2.使用 Histo

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable