Java JSON 序列化如何匹配 Python json.dumps() 结果

2018-10-23 14:12:17 +08:00
 corningsun

Java 项目对接 Python 服务端接口时,需要做参数 md5 校验

Python 中处理参数是通过 json.dumps() 方式先拿到请求 json 再计算 md5 值的

但是 Python 和 Java 序列化的结果不一致,导致 md5 验证无法通过

Python 代码示例

#!/usr/bin/env python3
# coding: utf-8

from json import dumps

if __name__ == '__main__':

    demo_bean = {
        "id": 1,
        "name": "demoName",
        "values": [1, 2, 3, 4]
    }
    demo_json = dumps(demo_bean, sort_keys=True).encode('utf-8')
    print(demo_json)

// 输出结果:
// b'{"id": 1, "name": "demoName", "values": [1, 2, 3, 4]}'

Java 我用的是 FastJson

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Lists;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.List;

@Slf4j
public class FastJsonTest {

    @Test
    public void testClean() throws Exception {
        DemoBean demoBean = DemoBean.builder()
                .id(1)
                .name("demoName")
                .values(Lists.newArrayList(1, 2, 3, 4))
                .build();

        String demoJson = JSON.toJSONString(demoBean, SerializerFeature.SortField);

        log.debug("demoJson={}", demoJson);
    }
}

@Data
@Builder
class DemoBean {
    private Integer id;
    private String name;
    private List<Integer> values;
}

// 输出
// demoJson={"id":1,"name":"demoName","values":[1,2,3,4]}

我比较了下两种方式的输出,主要是 Python 序列化结果多了一些 “空格”。

python: {"id": 1, "name": "demoName", "values": [1, 2, 3, 4]}
java  : {"id":1,"name":"demoName","values":[1,2,3,4]}

目前 Python 服务端代码不能变更,只能使 Java 的 json 对象序列化结果和 Python 的一致,有什么好的方式吗?

5436 次点击
所在节点    Java
23 条回复
linhua
2018-10-23 14:30:25 +08:00
https://stackoverflow.com/questions/16311562/python-json-without-whitespaces
https://docs.python.org/3/library/json.html#json.dump
If specified, separators should be an (item_separator, key_separator) tuple. The default is (', ', ': ') if indent is None and (',', ': ') otherwise. To get the most compact JSON representation, you should specify (',', ':') to eliminate whitespace.

手动将', '和': '替换成',',':'
myyou
2018-10-23 14:31:27 +08:00
json.dumps(demo_bean, sort_keys=True, separators=(',', ':'))
zacharyjia
2018-10-23 14:36:42 +08:00
楼上两位没好好看题呀,楼主说 Python 部分不能改了,只能想办法在 Java 里加了
misaka19000
2018-10-23 14:40:28 +08:00
改算法,JSON 类型的数据不应该因为空格就导致数据的 hash 结果不一致,更好的办法是根据 key 和 value 的值来计算 hash 的值
whileFalse
2018-10-23 14:49:32 +08:00
竟然不是排序 key 之后根据 key 和 value 自己写 hash …
xmt328
2018-10-23 16:10:03 +08:00
这个设计好有问题啊,环境依赖的这么严重,谁设计的不怕被打么
corningsun
2018-10-23 16:10:23 +08:00
@misaka19000 @whileFalse

是的,但是 Python 服务端现状就是这个样子了,没法让对方改了。

已经把 FastJson 源码看了一遍了,并没有找到设置“空格”的地方。。😢

```java
package com.alibaba.fastjson.serializer;

public class FieldSerializer implements Comparable<FieldSerializer> {

private final String double_quoted_fieldPrefix;
private String single_quoted_fieldPrefix;

public FieldSerializer(Class<?> beanType, FieldInfo fieldInfo){
...

this.double_quoted_fieldPrefix = '"' + fieldInfo.name + "\":";
...
}

public void writePrefix(JSONSerializer serializer) throws IOException {
SerializeWriter out = serializer.out;

if (out.quoteFieldNames) {
if (out.useSingleQuotes) {
if (single_quoted_fieldPrefix == null) {
single_quoted_fieldPrefix = '\'' + fieldInfo.name + "\':";
}
out.write(single_quoted_fieldPrefix);
} else {
out.write(double_quoted_fieldPrefix);
}
} else {
if (un_quoted_fieldPrefix == null) {
this.un_quoted_fieldPrefix = fieldInfo.name + ":";
}
out.write(un_quoted_fieldPrefix);
}
}
```

misaka19000
2018-10-23 16:15:04 +08:00
@corningsun #7 既然是这样那么比较挫的办法就是把所有的 ',' replace 为', ' 了
corningsun
2018-10-23 16:20:46 +08:00
@misaka19000
现在就是这么干的,但是有个字段是富文本,很容易把别的内容覆盖掉,所以来找更好的方法。
PulpFunction
2018-10-23 16:44:56 +08:00
上正则
或者在值后面加个 logo
比如{"id":1@#*,"name":"demoName@#*","values":[1@#*,2@#*,3@#*,4@#*]}
接着再用 1 楼的方案
PulpFunction
2018-10-23 16:46:13 +08:00
再富的文本也不能富过 logo 吧
嫌短再加长点
PulpFunction
2018-10-23 16:47:02 +08:00
我觉得 3 个###就行
PulpFunction
2018-10-23 16:48:42 +08:00
还有细节问题
PulpFunction
2018-10-23 16:54:11 +08:00
{"id":***1###,"name":"***demoName###","values":***[1###,2###,3###,4]}
PulpFunction
2018-10-23 16:55:12 +08:00
采纳记得发送感谢,每一个都发送啊,币不多了
kkkkkrua
2018-10-23 17:07:32 +08:00
String value = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(demoApplication);
System.out.println(value.replace("\r\n","").replace("{ ","{")); //最后的首{可以用正则匹配下中间的空格
kkkkkrua
2018-10-23 17:08:52 +08:00
思路是先美化输出,然后替换掉换行,再替换开头的{中间的空格
//美化输出的
{
"name" : "张三",
"age" : 18
}
//替换后的
{"name" : "张三", "age" : 18}
woodensail
2018-10-23 17:09:56 +08:00
不能拿到原始的 requestbody 直接进行 hash 吗?
kkkkkrua
2018-10-23 17:10:36 +08:00
忘记说了,包是 com.fasterxml.jackson.databind.ObjectMapper;不知道 fastjson 有没有美化方法
woodensail
2018-10-23 17:23:54 +08:00
或者可以这么干。请求就带俩参数,一个是 md5 一个是 data。data 就是 json 字符串。服务端拿到后直接对字符串进行 md5 校验,校验通过了对 data 进行解析得到真实参数。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/500208

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX