Jackson Json字符串和对象互转时key从配置文件中动态获取

本文介绍了在使用SpringBoot和Jackson进行JSON字符串与对象转换时,如何通过自定义属性命名策略和配置文件实现动态键值映射,以解决接口文档中键名不确定的问题。
摘要由CSDN通过智能技术生成

背景

  在json字符串和对象互转功能中,遇到了这样的情况,知道数据接口的要素是什么。但是不知道具体的key是什么。举个例子,知道接口中有“用户名 / 密码”,”两个要素,但是不知道对应key是“usename/password”,还是“uname/passwd”,还是“user_name/passwrod”。
  为了开发进度,就设想先把类和类中的属性创建出来,然后把属性对应的key配置到文件中,在json字符串和对象互转时,从配置文件获取类属性对应的json key。等拿到接口文档是,把配置文件做好,不用改代码就可以正常工作了。json工具类使用的是jackson,在导入springboot依赖时,会一起导入,不用单独导入这个依赖。

开发过程中问题

  最初准备使用@JsonProperty(“new-json-key”)注解,这个注解用在字段上,意思是用“new-json-key”
的值替换字段名。但有个问题,这个注解的值是个常量,不能随着配置动态变化。于是尝试修改成这样@JsonProperty(“${new-json-key}”),这在springboot的注解中很常见。下面就要来“但是"了.@JsonProperty不是springboot管理的注解,压根就不会替换。这条路就失败了。。。
  接着尝试使用自定义序列化/反序列化类 JsonSerializer/JsonDeserializer,经过测试,发现这两个类是针对“值“做自定义出入输出的。于当初的设想不一致
  最后找到了PropertyNamingStrategy类(属性命名策略),这个类是针对key做个性化处理的。下面就介绍一下实现过程。

实现过程

  • 配置文件设计
      配置文件以Bean做单位,每一个Bean配置一个文件。其中key是类的属性,value是实际的jsonkey
    例如,假定 "用户名"要素 key 为“user_name”,类中属性 定义为“userName”
    那么,先创建UserInfo类,类中有属性,userName
package com.ylink.cfcust.core.config.jackson;

import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Data;

/**
 * @create 2024/1/23 16:48
 */
@Data //lombok注解
@JsonNaming(MyPropertyNamingStrategy.class)//自定义属性命名策略
public class UserInfo {
    private String userName;
}

对应配置文件名为"UserInfo.properties",内容如下
UserInfo配置文件

  • 配置文件解析类
      用于解析所有配置文件,其中第一层结构是map,key为文件名(不包括后缀),第一层map的value是以配置文件的key-value为内容的map
package com.ylink.cfcust.core.config.jackson;

import com.ylink.cfcust.core.utils.PropertiesUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 加载配置文件
 */
@Component
public class JacksonKeyConfig {

	//配置文件的目录,可以配置在Application.yml文件里
    @Value("${jackson.json-keys.path:./config/jsonkeys}")
    private String configFilePath;

    private Map<String, Map<String, String>> jsonkeyMap = new HashMap<>();

    @PostConstruct//spring初始化实例后,执行本方法,自动加载所以配置文件
    public void init (){
        File fileDir = new File(configFilePath);
        if(!fileDir.exists()){
            return;
        }
        File[] props = fileDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    //过滤掉目录
                    return false;
                }
                //过滤非 PROPERTIES 文件                        
                return pathname.getName().toUpperCase().endsWith("PROPERTIES");
            }
        });
        if(props == null || props.length == 0){
            return;
        }

        for (File prop: props){
            Map<String, String> configMap = parsePropFile2Map(prop);
            String key = prop.getName().substring(0, prop.getName().lastIndexOf("."));
            jsonkeyMap.put(key, configMap);

        }
    }
    
	private Map<String, String> parsePropFile2Map(File file) {
        Properties prop = new Properties();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            prop.load(fis);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }

        if(prop != null){

            Map<String, String> valueMap = new HashMap<String, String>();

            Set<Object> keys = prop.keySet();
            Iterator<?> it = keys.iterator();
            while (it.hasNext()) {
                String key = String.valueOf(it.next());
                valueMap.put(key, prop.getProperty(key));
            }

            return valueMap;
        }
        return null;
    }
    public String getKeyMapping(String className, String key){
        if(!this.jsonkeyMap.containsKey(className)){
            return null;
        }
        return this.jsonkeyMap.get(className).get(key);
    }
}

  • jackson ObjectMapper 初始化类
package com.ylink.cfcust.core.config.jackson;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 
 */
@Configuration
public class ObjectMapperConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper;
    }
}

  • jackson 工具类 JacksonUtil
       这个类注入了ObjectMapper 和JacksonKeyConfig 的实例。提供了Json字符串和对象互转的方法
package com.ylink.cfcust.core.config.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * 
 * 工具类
 */
@Component
public class JacksonUtil {

    private static ObjectMapper objectMapper;

    private static JacksonKeyConfig jacksonKeyConfig;

    public static String bean2Str (Object bean) throws JsonProcessingException {
        if(bean == null){
            return null;
        }
        try {
            String jsonStr = objectMapper.writeValueAsString(bean);
            return jsonStr;
        } catch (JsonProcessingException e) {
            throw e;
        }
    }

    public static String bean2StrForamt (Object bean) throws JsonProcessingException {
        if(bean == null){
            return null;
        }
        try {
            String jsonStr = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean);
            return jsonStr;
        } catch (JsonProcessingException e) {
            throw e;
        }
    }

    public static <T> T jsonStr2Bean (String jsonStr, Class<T> cls) throws IOException {
        if(jsonStr == null || "".equalsIgnoreCase(jsonStr)){
            return null;
        }
        if(cls == null){
            return null;
        }
        try {
            T t = objectMapper.readValue(jsonStr, cls);
            return t;
        } catch (IOException e) {
            throw e;
        }
    }

    public static String getKeyMapping( String className, String key){
        if(className == null || key == null){
            return null;
        }
        return JacksonUtil.jacksonKeyConfig.getKeyMapping(className, key);
    }

    public static String getKeyMapping(String value) {
        if(value == null){
            return null;
        }
        String[] strArray = value.split("[.]");
        if(strArray == null || strArray.length < 2){
            return null;
        }

        return getKeyMapping(strArray[0], strArray[1]);
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        JacksonUtil.objectMapper = objectMapper;
    }

    @Autowired
    public void setJacksonKeyConfig(JacksonKeyConfig jacksonKeyConfig) {
        JacksonUtil.jacksonKeyConfig = jacksonKeyConfig;
    }
}

  • 自定义属性命名策略类
package com.ylink.cfcust.core.config.jackson;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @create 2024/1/24 18:03
 */
public class MyPropertyNamingStrategy extends PropertyNamingStrategy.SnakeCaseStrategy {

    /**
     * 用于序列化时 key 的重命名
     * @param config
     * @param method
     * @param defaultName   类中的属性名
     * @return
     */
    @Override
    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
        //正在处理bean对象的Class
        Class<?> clazz = method.getDeclaringClass();
        //不带包名的类名
        String className = clazz.getSimpleName();

        String realKeyName;
        //去配置中获取实际key
        String tmpKeyNmae = JacksonUtil.getKeyMapping(className, defaultName);

        if(tmpKeyNmae != null){
            //配置存在,使用配置值
            realKeyName = tmpKeyNmae;
        }else {
            //配置不存在,使用类中定义的原始属性名
            realKeyName = defaultName;
        }

        return realKeyName;
    }

    /**
     * 用于反序列化时 key 的重命名
     * @param config
     * @param method
     * @param defaultName   类中的属性名
     * @return
     */
    @Override
    public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
        //正在处理bean对象的Class
        Class<?> clazz = method.getDeclaringClass();
        //不带包名的类名,和配置文件不带后缀的we年明一致
        String className = clazz.getSimpleName();

        String realKeyName;
        //去配置中获取实际key
        String tmpKeyNmae = JacksonUtil.getKeyMapping(className, defaultName);
        if(tmpKeyNmae != null){
            //配置存在,使用配置值
            realKeyName = tmpKeyNmae;
        }else {
            //配置不存在,使用类中定义的原始属性名
            realKeyName = defaultName;
        }

        return realKeyName;
    }

}

   属性命名策略类的使用,是在类名加注解@JsonNaming(MyPropertyNamingStrategy.class)
属性命名策略类的使用

测试

测试代码

 public void test1() throws IOException {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("test");

        //bean转json字符串
        String studentStr = objectMapper.writeValueAsString(userInfo);
        System.out.println("bean 2 jsonStr ->"+studentStr);

        String jsonStr = "{\"user_name\":\"test\"}";
        //json字符串转对象
        UserInfo testBean1 = JacksonUtil.jsonStr2Bean(jsonStr, UserInfo.class);
        System.out.println("jsonStr["+jsonStr+"] 2 bean ->"+testBean1.toString());
    }

测试结果
测试结果

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将JSON字符串转换成Map对象,可以使用Java的相关库和方法。可以使用Jackson、Gson或FastJson等库来进行转换。 使用Jackson库的方法如下: 1. 首先,导入Jackson库的依赖。 2. 创建一个ObjectMapper对象,它是Jackson库的核心类,用于处理JSONJava对象之间的转换。 3. 使用ObjectMapper的readValue方法,将JSON字符串作为参数传入,并指定转换的目标类型为Map.class。 4. 将返回的Map对象用于后续的操作。 下面是一个示例代码: ``` import com.fasterxml.jackson.databind.ObjectMapper; public class JsonToMapExample { public static void main(String[] args) { String jsonString = "{\"key1\":\"value1\", \"key2\":\"value2\"}"; ObjectMapper objectMapper = new ObjectMapper(); try { Map<String, Object> map = objectMapper.readValue(jsonString, Map.class); System.out.println(map); } catch (IOException e) { e.printStackTrace(); } } } ``` 运行以上代码,将会输出转换后的Map对象。 另外,使用Gson和FastJson的方法也类似,只是使用的库和相应的类有所不同。你可以根据具体的需求选择适合的库和方法进行JSON字符串到Map对象的转换。 引用:[https://www.cnblogs.com/leigepython/p/11320438.html]<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [将JSON字符串转换成Map对象的方法](https://download.csdn.net/download/weixin_38519763/12988228)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [fastjsonjson字符串转化成map的五种方法](https://blog.csdn.net/agg7911/article/details/101137120)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值