V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NoKey
V2EX  ›  程序员

请教: json 字符串转 map 时,如何做到驼峰转下划线

  •  
  •   NoKey · 92 天前 · 1692 次点击
    这是一个创建于 92 天前的主题,其中的信息可能已经有所发展或是发生改变。
    试了几个库,都是 obj 转 json 的时候可以做到驼峰转下划线
    但是 json 转 map 的试试,无法转
    请问一下,有啥好办法可以转么?谢谢
    16 条回复    2024-08-22 09:48:28 +08:00
    a1b2c3T
        1
    a1b2c3T  
       92 天前
    在将 JSON 字符串转换为 Map 时,如果需要将 JSON 键的命名方式从驼峰命名法( Camel Case )转换为下划线命名法( Snake Case ),可以通过以下步骤实现:

    方法一:手动转换
    反序列化 JSON 字符串为 Map: 使用 Jackson 或 Gson 等库将 JSON 字符串反序列化为 Map 。
    遍历 Map 并修改键名: 遍历 Map ,将每个键名从驼峰格式转换为下划线格式。
    代码示例(使用 Jackson ):
    java
    复制代码
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.util.HashMap;
    import java.util.Map;

    public class CamelToSnake {
    public static void main(String[] args) throws Exception {
    String jsonString = "{\"userName\":\"JohnDoe\",\"userAge\":30}";

    ObjectMapper objectMapper = new ObjectMapper();
    // 反序列化 JSON 字符串为 Map
    Map<String, Object> originalMap = objectMapper.readValue(jsonString, HashMap.class);

    Map<String, Object> resultMap = new HashMap<>();

    // 遍历 Map ,将驼峰转换为下划线格式
    for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
    String snakeKey = camelToSnake(entry.getKey());
    resultMap.put(snakeKey, entry.getValue());
    }

    // 输出转换后的 Map
    System.out.println(resultMap);
    }

    // 将驼峰命名转为下划线命名
    private static String camelToSnake(String camelCaseStr) {
    StringBuilder result = new StringBuilder();
    for (char c : camelCaseStr.toCharArray()) {
    if (Character.isUpperCase(c)) {
    result.append("_").append(Character.toLowerCase(c));
    } else {
    result.append(c);
    }
    }
    return result.toString();
    }
    }
    输出结果:
    java
    复制代码
    {user_name=JohnDoe, user_age=30}
    方法二:使用 Jackson 自定义策略
    如果你希望自动将 JSON 中的字段从驼峰转换为下划线,可以使用 Jackson 的自定义命名策略来实现。

    代码示例:
    java
    复制代码
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.PropertyNamingStrategies;
    import java.util.Map;

    public class JacksonCamelToSnake {
    public static void main(String[] args) throws Exception {
    String jsonString = "{\"userName\":\"JohnDoe\",\"userAge\":30}";

    // 创建 ObjectMapper 并设置命名策略为下划线
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

    // 反序列化 JSON 字符串为 Map
    Map<String, Object> resultMap = objectMapper.readValue(jsonString, Map.class);

    // 输出转换后的 Map
    System.out.println(resultMap);
    }
    }
    输出结果:
    java
    复制代码
    {user_name=JohnDoe, user_age=30}
    说明
    方法一 适用于需要手动控制转换过程的场景,可以灵活处理不同的命名转换规则。
    方法二 使用 Jackson 的内置命名策略,可以自动将驼峰格式转换为下划线格式,非常方便,适用于更大规模的项目。
    两种方法可以根据你的需求选择合适的实现方式。
    a1b2c3T
        2
    a1b2c3T  
       92 天前
    @a1b2c3T #1 来自 gpt 的回答
    NoKey
        3
    NoKey  
    OP
       92 天前
    @a1b2c3T 方案 2 我试了,不行,也是 obj 转 json 可以,json 转 obj 不行。。。
    chendy
        4
    chendy  
       92 天前
    以下内容基于你用的是 java:
    既然都 Map 了就不用转换了,直接 get("xxx_bbb")
    甚至 Map 都不用,有一个神奇的包里面有个神奇的类叫 JSONObject ,直接各种 get(String) 就行

    如果真的要 Map 且真的要转换,遍历 key ,改 key ,塞进另外一个新 map ,也不难
    chendy
        5
    chendy  
       92 天前   ❤️ 1
    随手写一个,然后下班

    ```java
    class Scratch {
    public static void main(String[] args) {
    HashMap<String, Object> map = new HashMap<>();
    map.put("userName", "user01");
    map.put("userAge", 18);
    map.put("userFriends", Arrays.asList("user02", "user03"));
    System.out.println(map);
    Map<String, Object> newMap = convertMap(map);
    System.out.println(newMap);
    }

    public static Map<String, Object> convertMap(Map<String, ?> map) {
    HashMap<String, Object> newMap = new HashMap<>();
    for (Map.Entry<String, ?> e : map.entrySet()) {
    String key = e.getKey();
    String newKey = convertKey(key);
    newMap.put(newKey, e.getValue());
    }
    return newMap;
    }

    public static String convertKey(String key) {
    StringBuilder b = new StringBuilder(key.length() * 2);
    for (int i = 0; i < key.length(); i++) {
    char c = key.charAt(i);
    if (Character.isUpperCase(c)) {
    b.append("_").append(Character.toLowerCase(c));
    } else {
    b.append(c);
    }
    }
    return b.toString();
    }
    }```
    Ayanokouji
        6
    Ayanokouji  
       92 天前   ❤️ 1
    实在想不明白,为什么会有这种需求。
    twofox
        7
    twofox  
       92 天前
    jackson 示例

    先把 json 字符串转为 JsonNode ,然后递归遍历所有属性,处理所有的 key ,复制到新的 objectNode 不就可以了

    ```java
    public ObjectNode convertJsonToUnderline(JsonNode jsonNode){
    if (jsonNode==null){
    return null;
    }
    ObjectNode result = objectMapper.createObjectNode();
    jsonNode.properties().forEach((entry -> {
    String key = entry.getKey();
    StrUtil.toUnderlineCase(key);
    result.set(key, convertJsonToUnderline(entry.getValue()));
    }));
    return result;
    }
    ```
    twofox
        8
    twofox  
       92 天前
    @twofox 噢,有点问题,改一下才行
    zsj1029
        9
    zsj1029  
       92 天前
    一般 java c# 都是拿反射的 注解实现的 ,js 得用 ts 的开启装饰器 做
    twofox
        10
    twofox  
       92 天前
    修改后的改后的代码

    public static JsonNode convertJsonToUnderline(JsonNode jsonNode){
    if (!(jsonNode instanceof ObjectNode)){
    return jsonNode;
    }
    ObjectNode result = objectMapper.createObjectNode();
    jsonNode.properties().forEach((entry -> {
    String key = entry.getKey();
    result.set(StrUtil.toUnderlineCase(key), convertJsonToUnderline(entry.getValue()));
    }));
    return result;
    }


    原始数据:{"code":0,"data":{"myStartCount":35,"todoCount":0,"doneCount":0},"msg":""}
    转换后的数据:
    {
    "code" : 0,
    "data" : {
    "my_start_count" : 35,
    "todo_count" : 0,
    "done_count" : 0
    },
    "msg" : ""
    }
    Huelse
        11
    Huelse  
       92 天前
    要么先用正则匹配键值替换,然后再塞进 map ,要么转成 map 后再修改键值
    vialon17
        12
    vialon17  
       92 天前
    @Ayanokouji 一样感觉很奇怪的需求,做个映射都好点,
    CEBBCAT
        13
    CEBBCAT  
       92 天前
    @a1b2c3T #1 GPT 回复很可能被 MOD 封号
    NoKey
        14
    NoKey  
    OP
       92 天前
    @Ayanokouji 需求来源就是,json 字符串直接入库,不用 entity 映射,入库的试试,需要字段名和数据表字段一模一样,json 转 map ,map 入库,这样可以写一个通用的入库方法
    Ayanokouji
        15
    Ayanokouji  
       92 天前
    @NoKey 那这跟 json 还是没关系,还是想偷懒,bean 对象至少还有注解等原数据做转换。遇到转换后的 key 和数据库的字段还是不匹配,你打算怎么解决。比如 XMLHttpRequest 这种这类的。
    a1b2c3T
        16
    a1b2c3T  
       92 天前
    @CEBBCAT #13 啊我去,不知道还有这个规定啊。版规在哪儿呢,我去学习一下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5628 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 06:30 · PVG 14:30 · LAX 22:30 · JFK 01:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.