关于JSON字符串中的字段名与Java类中的字段名不匹配的解决方法(@JsonProperty(““) 与@JSONField(name = ““)的使用 )

1.1 背景

1.1.1 所谓的背景,也就是写这篇博客目的与原因是什么呢?

        最近在工作中遇到了一个非常奇葩的需求,通过WebService的方式去调第三方接口,但第三方接口的返回值是一个Xml结构,本来到这里并没什么奇怪。但接下来意外发生了,这个Xml结构的字段名是中文,而且我所需的节点中的数据还不是个JSON结构。所以我需要先将Xml节点中的数据解析出来,然后使用split进行分割,转化成JSON结构,最终对JSON做自定义对象的映射。但有个问题,我需要将该字段中文与Java实体字段相匹配,存到数据库中。但由于字段太多,如果用枚举或者if等方式一个一个匹配太麻烦了,还不够简洁,就想着有没有一个注解可以解决这个问题?

        首先我第一个想到的是MyBatis-Plus中,@TableField注解,但该注解主要用于实体类字段与数据库表字段的映射,与我的需求不符合(哈哈,因为毕业不久,我之前没遇到过这个问题,所以@JsonProperty("") 与@JSONField(name = "")这俩个注解我也是刚刚了解)。

        接着通过查阅了解到@JsonProperty("") 与@JSONField(name = "")的使用,经过测试我最终选用的是@JsonProperty("")。

1.2 关于@JsonProperty("")

1.2.1 简介

@JsonProperty 是 Jackson 库中的一个注解,用于在反序列化 JSON 数据时,将 JSON 对象中的某个属性映射到 Java 类中的某个字段上。

@JsonProperty 的语法格式为 @JsonProperty("propertyName"),其中 propertyName 是 JSON 对象中的属性名,也就是要映射到 Java 类中的字段名。

使用 @JsonProperty 注解可以确保 JSON 数据和 Java 类之间的字段名匹配,从而正确地进行反序列化。例如,如果 JSON 数据中有一个属性名为 "name",那么在 Java 类中应该有一个名为 "name" 的字段,并且使用 @JsonProperty("name") 注解进行标记,这样 Jackson 库就可以正确地将 JSON 数据中的 "name" 属性映射到 Java 类中的 "name" 字段上。

除了指定属性名之外,@JsonProperty 还支持一些其他的选项,例如可以设置字段的顺序、是否可序列化、是否可反序列化等。

总之,@JsonProperty 是 Jackson 库中用于反序列化 JSON 数据时进行字段映射的重要注解之一。

1.2.2 举个例子(@JsonProperty 不仅可以映射英文,还可以映射中文)

注:不要引错了依赖:import com.fasterxml.jackson.annotation.JsonProperty;

这里我就以映射中文举例了:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserBo implements Serializable {

    @JsonProperty("姓名")
    private String name;
    @JsonProperty("电话")
    private String phone;
    @JsonProperty("城市")
    private String city;

}
/**
 * @Author ZhaoShuHao
 * @Date 2023/11/11 15:09
 */
public class UserController {
    public static void main(String[] args) throws IOException {
        String user = "{\"姓名\":\"张三\",\"城市\":\"北京\",\"电话\":\"1212323\"}";
//        UserBo userBo1 = JSONObject.parseObject(user, UserBo.class);
        ObjectMapper objectMapper = new ObjectMapper();
        UserBo userBo1 = objectMapper.readValue(user, UserBo.class);
        System.out.println(userBo1);
    }
}

1.3 关于@JSONField(name = "")

1.3.1 简介

@JSONField(name = "") 是 Jackson 库中的一个注解,用于在序列化或反序列化 JSON 数据时,将 Java 类中的某个字段映射到 JSON 对象中的某个属性上。

@JSONField 注解的语法格式为 @JSONField(name = "property_name"),其中 name 是 JSON 对象中的属性名,也就是要映射到 Java 类中的字段名。

使用 @JSONField 注解可以确保 Java 类和 JSON 数据之间的字段名匹配,从而正确地进行序列化或反序列化。例如,如果 Java 类中有一个名为 "name" 的字段,那么在 JSON 数据中应该有一个属性名为 "property_name",并且使用 @JSONField(name = "property_name") 注解进行标记,这样 Jackson 库就可以正确地将 Java 类中的 "name" 字段映射到 JSON 数据中的 "property_name" 属性上。

除了指定属性名之外,@JSONField 还支持一些其他的选项,例如可以设置字段的顺序、是否可序列化、是否可反序列化等。

总之,@JSONField 是 Jackson 库中用于序列化或反序列化 JSON 数据时进行字段映射的重要注解之一。

1.3.2 举个例子

注:查阅资料,@JSONField也可以映射中英文,但我映射中文没有成功,暂时没有找到问题所在,这里就以映射英文举例了

import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserBo implements Serializable {

    @JSONField(name = "names")
//    @JsonProperty("姓名")
    private String name;
    @JSONField(name = "phones")
//    @JsonProperty("电话")
    private String phone;
    @JSONField(name = "citys")
//    @JsonProperty("城市")
    private String city;

}
/**
 * @Author ZhaoShuHao
 * @Date 2023/11/11 15:09
 */
public class UserController {
    public static void main(String[] args) throws IOException {
//        String user = "{\"姓名\":\"张三\",\"城市\":\"北京\",\"电话\":\"1212323\"}";
        String user = "{\"names\":\"张三\",\"citys\":\"北京\",\"phones\":\"1212323\"}";
        UserBo userBo1 = JSONObject.parseObject(user, UserBo.class);
     /*   ObjectMapper objectMapper = new ObjectMapper();
        UserBo userBo1 = objectMapper.readValue(user, UserBo.class);*/
        System.out.println(userBo1);
    }
}

1.4 对于我所遇到的需求的解决方案

具体就不说了,直接步入正题。

1.4.1 结构

看看我的结构(不光是中文,结构还有些差异☺):

(1)

<Response>
    <resultCode>0</resultCode>
    <resultMessage>
        病例数:10,
        费用极高病例:0,
        费用极低病例:0,
        医疗总费用(元):507753.35,
        DRG支付标准(元):457775.76,
        盈亏额(元):-15109.75,
        次均盈亏额(元):-1510.98,
        DRG支付费用(元):182516.69,
        CMI:1,
        总权重:42.8062,
        入组率:80.00%,
        DRG组数:4,
        盈余病组:6,
        亏损病组:4,
        平均住院日:12.3,
        药品费(元):100457.29,
        药占比:19.66%,
        耗材费(元):164819.55,
        耗占比:32.46%,
        检查费(元):37136,
        检查费占比:7.31%,
        检验费(元):75670.5,
        检验费占比:14.90%,
        时间消耗指数:1.06,
        费用消耗指数:1.11
    </resultMessage>
</Response>

(2)这个更坑,key不仅是中文,还多了几个字,转化完Json,还要截取(刚开始没发现)

<Response>
    <resultCode>0</resultCode>
    <resultMessage>
        近一月:入径人数:37,
        完成人数:37,
        出径人数:0,
        出院人数:47,
        变异人数:0
    </resultMessage>
</Response>

1.4.2 解决方案

(1)先对xml进行解析,获取到resultMessage节点中的数据

(2)将节点中的数据拼接成一个字符串,去掉换行符

(3)因为解析出的数据不符合JSON格式,所以我们先用split进行分割,重新组装成Map结构

(4)将Map结构转化为Json字符串

(5)将Json字符串与自定义对象做映射

(6)因为好几个接口都需要解析xml,但映射的对象不同,返回类型不同,所以我这里用了泛型和反射

(7)又因为接口返回的xml中,数据的结构还有差异,所以我们还需一个类型,来判断是否需要做特殊处理

大功告成,代码如下:

package com.lc.ibps.medical.utils;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lc.ibps.medical.enums.BizDataMethodEnums;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

/**
 * Xml的解析工具类,根据泛型,动态返回不同的对象
 * @Author ZhaoShuHao
 * @Date 2023/11/10 15:52
 */
@Slf4j
public class ParseXmlUtils {

    /**
     * 解析xml为对应的对象
     * @param clazz 自定义对象的类
     * @param xmlString 需要解析的xml对象
     * @param code 接口标识(因为个别接口返回的结构不一样,所以这里对特别的结构进行处理)
     * @param <T>   自定义对象
     * @return
     * @throws Exception
     */
    public static  <T> T getData(Class<T> clazz ,String xmlString,String code) throws Exception {
        //根据泛型,动态返回不同的对象
        T obj = clazz.getDeclaredConstructor().newInstance();
        // 创建一个 DocumentBuilderFactory 实例
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        // 创建 DocumentBuilder 实例
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        InputSource is = new InputSource(new StringReader(xmlString));
        // 使用 parse 方法解析输入源
        Document doc = dBuilder.parse(is);
        // 检查解析是否成功
        if (doc != null) {
            log.info("解析输入源成功:"+doc);
            // 获取所有 name 节点
            NodeList nodes = doc.getElementsByTagName("resultMessage");
            StringBuilder stringBuilder = new StringBuilder();
            // 遍历所有的 name 节点
            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                // 获取每个节点的文本内容
                stringBuilder.append(node.getTextContent());
            }
            //因为获取到的信息不符合Json格式,所以不能直接转化为Josn,或者通过JSON转其他格式,对字符串进行处理
            // 解析字符串为 Map
            String oldStr = String.valueOf(stringBuilder);
            String str = oldStr.replaceAll("\\n", "");
            //TODO (等接口调通还需确认)因为,个人临床路径数据和个人电子病历数据返回的结构有点变化,所以这里进行处理,去掉多余字符
            if(BizDataMethodEnums.MES0735.getCode().equals(code)||BizDataMethodEnums.MES0736.getCode().equals(code)){
                str = str.substring(4);
            }
            log.info("xml转字符串:"+str);
            Map<String, Object> map = new HashMap<>();
            String[] pairs = str.split(",");
            for (String pair : pairs) {
                String[] keyValue = pair.split(":");
                //解决value不存在的情况,默认给他设置为null
                if(keyValue.length==1){
                    map.put(keyValue[0], null);
                }else {
                    map.put(keyValue[0], keyValue[1]);
                }
            }
            log.info("字符串转map:"+map);
            // 将 Map 转换为 JSON 字符串
            String jsonString = JSON.toJSONString(map);
            log.info("map转Json:"+jsonString);
            ObjectMapper objectMapper = new ObjectMapper();
            //根据不同的类型返回不同的对象
            obj = (T) objectMapper.readValue(jsonString, obj.getClass());
            log.info("Json映射对象:"+obj);
        }
        return obj;
    }
}
@ApiModel(value = "DRG质量数据对象")
public class DrgYearTbl extends AbstractPo<String>{
			@ApiModelProperty(value = "主键")
			@JsonProperty("主键")
			protected String  id; 		/*主键*/
			@ApiModelProperty(value = "外键")
			@JsonProperty("外键")
			protected String  parentId; 		/*外键*/
			@ApiModelProperty(value = "租户ID")
			@JsonProperty("租户ID")
			protected String  tenantId; 		/*租户ID*/
			@ApiModelProperty(value = "IP地址")
			@JsonProperty("IP地址")
			protected String  ip; 		/*IP地址*/
			@ApiModelProperty(value = "数据删除状态")
			@JsonProperty("数据删除状态")
			protected String  deleted; 		/*数据删除状态*/
			@ApiModelProperty(value = "版本")
			@JsonProperty("版本")
			protected Long  version; 		/*版本*/
			@ApiModelProperty(value = "序号")
			@JsonProperty("序号")
			protected Long  orderNo; 		/*序号*/
			@ApiModelProperty(value = "流程状态")
			@JsonProperty("流程状态")
			protected String  flowStatus; 		/*流程状态*/
			@ApiModelProperty(value = "部门")
			@JsonProperty("部门")
			protected String  sysDeptId; 		/*部门*/
			@ApiModelProperty(value = "公司")
			@JsonProperty("公司")
			protected String  sysOrgId; 		/*公司*/
			@ApiModelProperty(value = "姓名")
			@JsonProperty("姓名")
			protected String  name; 		/*姓名*/
			@ApiModelProperty(value = "工号")
			@JsonProperty("工号")
			protected String  number; 		/*工号*/
			@ApiModelProperty(value = "年度")
			@JsonProperty("年度")
			protected String  year; 		/*年度*/
			@ApiModelProperty(value = "CMI")
			@JsonProperty("CMI")
			protected String  cmi; 		/*CMI*/
			@ApiModelProperty(value = "平均住院日")
			@JsonProperty("平均住院日")
			protected String  pjzyr; 		/*平均住院日*/
			@ApiModelProperty(value = "入组率")
			@JsonProperty("入组率")
			protected String  rzl; 		/*入组率*/
			@ApiModelProperty(value = "药占比")
			@JsonProperty("药占比")
			protected String  yzb; 		/*药占比*/
			@ApiModelProperty(value = "耗占比")
			@JsonProperty("耗占比")
			protected String  hzb; 		/*耗占比*/
			@ApiModelProperty(value = "次均费")
			@JsonProperty("次均费")
			protected String  cjf; 		/*次均费*/
			@ApiModelProperty(value = "检查占比")
			@JsonProperty("检查占比")
			protected String  jczb; 		/*检查占比*/
			@ApiModelProperty(value = "耗材费(元)")
			@JsonProperty("耗材费(元)")
			protected String  hcf; 		/*耗材费(元)*/
			@ApiModelProperty(value = "检查费(元)")
			@JsonProperty("检查费(元)")
			protected String  jcf; 		/*检查费(元)*/
			@ApiModelProperty(value = "检查费占比")
			@JsonProperty("检查费占比")
			protected String  jcfzb; 		/*检查费占比*/
			@ApiModelProperty(value = "检验费(元)")
			@JsonProperty("检验费(元)")
			protected String  jyf; 		/*检验费(元)*/
			@ApiModelProperty(value = "检验费占比")
			@JsonProperty("检验费占比")
			protected String  jyfzb; 		/*检验费占比*/
			@ApiModelProperty(value = "时间消耗指数")
			@JsonProperty("时间消耗指数")
			protected String  sjxhzs; 		/*时间消耗指数*/
			@ApiModelProperty(value = "费用消耗指数")
			@JsonProperty("费用消耗指数")
			protected String  fyxhzs; 		/*费用消耗指数*/
			@ApiModelProperty(value = "病例数")
			@JsonProperty("病例数")
			protected String  bls; 		/*病例数*/
			@ApiModelProperty(value = "费用极高病例")
			@JsonProperty("费用极高病例")
			protected String  fyjgbls; 		/*费用极高病例*/
			@ApiModelProperty(value = "费用极低病例")
			@JsonProperty("费用极低病例")
			protected String  fyjdbls; 		/*费用极低病例*/
			@ApiModelProperty(value = "医疗总费用(元)")
			@JsonProperty("医疗总费用(元)")
			protected String  ylzfy; 		/*医疗总费用(元)*/
			@ApiModelProperty(value = "DRG支付标准(元)")
			@JsonProperty("DRG支付标准(元)")
			protected String  drgzfbz; 		/*DRG支付标准(元)*/
			@ApiModelProperty(value = "盈亏额(元)")
			@JsonProperty("盈亏额(元)")
			protected String  yke; 		/*盈亏额(元)*/
			@ApiModelProperty(value = "次均盈亏额(元)")
			@JsonProperty("次均盈亏额(元)")
			protected String  cjyke; 		/*次均盈亏额(元)*/
			@ApiModelProperty(value = "DRG支付费用(元)")
			@JsonProperty("DRG支付费用(元)")
			protected String  drgzffy; 		/*DRG支付费用(元)*/
			@ApiModelProperty(value = "总权重")
			@JsonProperty("总权重")
			protected String  zqz; 		/*总权重*/
			@ApiModelProperty(value = "DRG组数")
			@JsonProperty("DRG组数")
			protected String  drgzs; 		/*DRG组数*/
			@ApiModelProperty(value = "盈余病组")
			@JsonProperty("盈余病组")
			protected String  ykbz; 		/*盈余病组*/
			@ApiModelProperty(value = "亏损病组")
			@JsonProperty("亏损病组")
			protected String  ksbz; 		/*亏损病组*/
			@ApiModelProperty(value = "药品费(元)")
			@JsonProperty("药品费(元)")
			protected String  ypf; 		/*药品费(元)*/
			@ApiModelProperty(value = "月度")
			@JsonProperty("月度")
			protected String  month; 		/*月度*/

		//省略get、set方法

	}
/**调用该方法的代码,只粘贴有关的内容:**/
String result = "<Response>\n" +
				"    <resultCode>0</resultCode>\n" +
				"    <resultMessage>病例数:10,\n" +
				"费用极高病例:0,\n" +
				"费用极低病例:0,\n" +
				"医疗总费用(元):507753.35,\n" +
				"DRG支付标准(元):457775.76,\n" +
				"盈亏额(元):-15109.75,\n" +
				"次均盈亏额(元):-1510.98,\n" +
				"DRG支付费用(元):182516.69,\n" +
				"CMI:,\n" +
				"总权重:42.8062,\n" +
				"入组率:80.00%,\n" +
				"DRG组数:4,\n" +
				"盈余病组:6,\n" +
				"亏损病组:4,\n" +
				"平均住院日:12.3,\n" +
				"药品费(元):100457.29,\n" +
				"药占比:19.66%,\n" +
				"耗材费(元):164819.55,\n" +
				"耗占比:32.46%,\n" +
				"检查费(元):37136,\n" +
				"检查费占比:7.31%,\n" +
				"检验费(元):75670.5,\n" +
				"检验费占比:14.90%,\n" +
				"时间消耗指数:1.06,\n" +
				"费用消耗指数:1.11</resultMessage>\n" +
				"</Response>";
                DrgYearTbl drgYearTbl = new DrgYearTbl();
				try {
					drgYearTbl=ParseXmlUtils.getData(DrgYearTbl.class,result,BizDataMethodEnums.MES0734.getCode());
				} catch (Exception e) {
					log.error("xml转对象出现异常:"+e);
				}

1.5 扩展(自定义工具类)

public class ObjectConvertUtil {

    /**
     * @param sourceClass 源对象
     * @param targetClass 目的对象class
     * */
    public static <T,U> U objectConvert(T sourceClass, Class<U> targetClass){
        String sourceStr= JSON.toJSONString(sourceClass);
        return JSONObject.parseObject(sourceStr,targetClass);
    }

}

想转什么对象,直接使用工具类去转就好了,省去很多麻烦,举个例子:

UserPo userpo = new UserPo();
User user = ObjectConvertUtil.objectConvert(userpo, User.class);

DogVo dogvo= new DogVo();
Dog dog= ObjectConvertUtil.objectConvert(dogvo, Dog.class);

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值