json 协议有规定连续两次序列化一个对象应该怎么样吗?

json 协议有规定连续两次序列化一个对象应该怎么样吗?

子问题:

  • python 如果反序列化一个「连续两次序列化后的字符串」
  • java 如果反序列化一个「连续两次序列化后的字符串」

有一个 python 的 dict

content = {
    'name': '你好'
}

我调用两次 json.dumps()

import json

content = {
    'name': '你好'
}

print(json.dumps(json.dumps(content)))

输出如下

"{\"name\": \"\\u4f60\\u597d\"}"

然后我调用两次 json.loads,这个「被连续两次序列化后的字符串」就会变回 dict

import json

content = {
    'name': '你好'
}

print(json.dumps(json.dumps(content)))

a = json.dumps(json.dumps(content))

print(json.loads(json.loads(a)))

输出如下:

"{\"name\": \"\\u4f60\\u597d\"}"
{'name': '你好'}

一切都非常的优雅

但是当我 python 把 "{\"name\": \"\\u4f60\\u597d\"}" 传递给 java 同事的时候,他们告诉我,他们无法「简单」实现「把上面的字符串连续两次反序列化变成一个 hashmap」,他们说如果要反序列化成 hashmap 怎么怎么麻烦

我不理解,这么成熟的编程语言 java 为什么不能简单优雅的实现这一点?


我把这个需求「使用 java 反序列化 "{\"name\": \"\\u4f60\\u597d\"}" 为 hashmap」问 chatGPT,它给的答案也是莫名其妙

为什么?是 java 的问题还是说 json 的协议就对于「连续两次序列化」没有做出标准规定导致不同编程语言自己有自己的实现?


有谁知道,像 golang 和 rust、js 这些语言又是怎么处理这个问题呢?

阅读 3.1k
avatarAI BotBETA

问题:json 协议有规定连续两次序列化一个对象应该怎么样吗?

答案:JSON 协议本身并没有规定连续两次序列化一个对象应该如何处理。然而,不同的编程语言和库可能会有自己的实现方式。

对于 Python,你可以使用 json.dumps()json.loads() 方法进行序列化和反序列化操作。如果你连续两次调用 json.dumps(),实际上会先将对象序列化为一个字符串,然后再将这个字符串序列化为一个新的 JSON 对象。这样会使得结果字符串包含了一个嵌套的 JSON 对象,而不是一个简单的 JSON 字符串。

对于 Java,情况则有所不同。Java 的 GsonJackson 等库通常只支持一次序列化和反序列化操作。如果你尝试连续两次进行序列化或反序列化,可能会遇到问题,因为这些库可能无法正确解析嵌套的 JSON 对象。

要解决这个问题,你可以选择一种适合你的编程语言和库的方法来处理连续的序列化和反序列化操作。在 Python 中,你可以使用 json.loads() 来将嵌套的 JSON 对象转换为 Python 字典。在 Java 中,你可以使用一个支持嵌套 JSON 对象的库或自定义解析器来处理连续的序列化和反序列化操作。

至于其他编程语言(如 Golang 和 Rust)的处理方式,具体情况可能会有所不同,但一般来说,这些语言也提供了类似 Python 和 Java 的序列化和反序列化库或函数。对于连续的序列化和反序列化操作,你需要查阅相关语言的文档或库的文档,以了解具体的实现方式和注意事项。

8 个回答

一次序列化的结果是 string ,两次序列化不过就是把 string 根据 json 序列化 string 的规则又序列化了一遍。

对于 Java 这一个强类型语言来说,表示 Json 这个各种类型混合的对象本身就是一件很麻烦的事情。所以不论你是一次序列化还是两次,都有很多麻烦事要搞。

1、一定要序列化两次吗,一次不就是一个 json string了吗?
2、如果一定要序列化两次,那第二次的结果其实就是一个string了,把里面的 \" 全部替换成 ",再解析就可以了

json 协议有规定连续两次序列化一个对象应该怎么样吗?

没有,也不需要,就是简单的套娃,套娃几次拆几次就回去了

无法「简单」实现「把上面的字符串连续两次反序列化变成一个 hashmap」

不能说很麻烦但是也不简单,起码框架自己没这功能给反序列化两次,但是自己写也就一行的事情
顺便吐槽一句,随便飞 hashmap 的 java 农都应该去写 php

这不是很简单的问题吗?某些写java的人是不是被spring惯坏了,最基本的代码都不会了

下面是用 gson 实现的一个简单的例子

Gson gson = new Gson();
Sring s = gson.fromJson(your_python_dumps_dumps_str, String.class);
TypeToken<Map<String, String>> mapType = new TypeToken<Map<String, String>>(){};
Map<String, String> stringMap = gson.fromJson(s, mapType);
System.out.println(stringMap.get("name"))

如果要转为 data calss User(val name:String),只需要把 mapType 换成 User.class

顺便吐槽一下, java 的代码是真的又臭又长,我用 kotlin 写就没那么多冗余代码

1.对象序列化两次这件事本身很异常,序列化一次之后就是字符串了,不明白为什么还要对字符串序列化
2.这是一个使用FastJson反序列化的例子

    public static void main(String[] args) {
        Object parse = JSON.parse("\"{\\\"name\\\": \\\"\\\\u4f60\\\\u597d\\\"}\"");
        Object parse1 = JSON.parse((String) parse);
        System.out.println(parse1);
    }

image.png
该例子中,parse1是JSONObject类型的对象。JSONObject是Map类的一个实现,应该是满足需求的

首先,我不明白为什么要序列化两次。第一次序列化会得到一个 JSON,这是一个描述了完整结构化数据的字符串。再次序列化其实是获得一个字符串的 JSON,也就是字符串的字符串。

我猜是因为要两次序列化的结果才能直接从控制台拷贝到代码中使用,是个很伤心的理由,希望不是这个原因 😭。

如果只是为了传输用,第一次序列化的结果就已经够用了。对方收到这个字符串之后,直接反序列化就能得到对应的结构化数据。不过既然序列化了两次,对方得到这个数据的时候,也需要反序列化两次,这不找事儿么 😂

然后,不管是哪里生成的 JSON,只有人工拼的比较容易出错,工具处理出来的一般都遵循标准 (json.org)。既然是标准的 JSON,Java 里的 JSON 库都是可以处理的,比如 Jackson、Gson 等库都能处理(实测处理起来有点区别)。

以实例来说,假如有这样一个数据(第一行是第一次序列化的结果,第二行是再次序列化的结果)

{"name":"James","age":20,"money":123.45}
"{\"name\":\"James\",\"age\":20,\"money\":123.45}"

生成这两个数据的代码 (Java/Jackson) 如下(不要在意 Person 的结构,就是包含 name、age 和 money 几个属性的对象):

snipaste_2024-01-11_16-58-17.png

如果要对 json 直接赋值,确实需要转义,拷贝的话需要使用第二个结果。但它的内容实际是第一个结果:

snipaste_2024-01-11_17-01-25.png

在 Java 中要把这个字符串反序列化成 HashMap,只需要用 json(第一次序列化结果)就行(以 Jackson 为例)

snipaste_2024-01-11_17-03-25.png

如果要想从 json2 反序列化出来,需要两次(以 Gson 为例)

snipaste_2024-01-11_17-06-15.png

提醒一下

这张图和上面那张相比,age 的类型不一样;Jackson 会根据数值来识别类型为 Integer,但 Gson 直接用的 Double 作为对应于 number 的标准类型。两种处理策略各有优劣,自己根据情况来选择吧。

完整的示例代码如下 (Java):

package playground.sf.q1010000044532110;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;

import java.util.HashMap;
import java.util.Map;

public class Solution {
    public static void main(String[] args) throws JsonProcessingException {
        var objectMapper = new ObjectMapper();
        var json = objectMapper.writeValueAsString(new Person("James"));
        // String json = "{\"name\":\"James\",\"age\":20,\"money\":123.45}";
        System.out.println(json);
        var json2 = objectMapper.writeValueAsString(json);
        System.out.println(json2);

        HashMap<String, Object> map1 = objectMapper.readValue(json, HashMap.class);
        System.out.println(map1);
        dumpMap(map1);

        Gson gson = new Gson();
        var gotJson = gson.fromJson(json2, String.class);
        System.out.println(gotJson);

        HashMap<String, Object> map2 = gson.fromJson(gotJson, HashMap.class);
        System.out.println(map2);

        dumpMap(map2);
    }

    static void dumpMap(@NotNull Map<String, Object> map) {
        map.forEach((key, value) -> System.out.printf(
            "%s = %s (%s)%n",
            key,
            value, value.getClass().getSimpleName()
        ));
    }
}

class Person {
    private final String name;
    private final int age;
    private final double money;

    public Person(String name) {
        this.name = name;
        age = 20;
        money = 123.45;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public double getMoney() { return money; }
}

所以序列化两次的意义是什么?
什么时候需要连续序列化两次呢?

应该直接可以把IO中的字节码序列化成对象

新手上路,请多包涵

image.png
包上一层就好了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题