英文:
Why are the results of Go and Rust serialization inconsistent?
问题
我想要的结果如下,但 Rust 的结果是另外一个。
[123 34 66 111 100 121 34 58 34 97 71 86 115 98 71 56 61 34 125]
Golang 示例:
type Response struct {
Body []byte
}
func Test_Marshal(t *testing.T) {
body := "hello"
resp := &Response{
Body:[]byte(body),
}
t.Log("resp:", resp)
result, _ := json.Marshal(resp)
t.Log("result: ", result)
}
Golang 结果:
aa_test.go:17: resp: &{[104 101 108 108 111]}
aa_test.go:19: result: [123 34 66 111 100 121 34 58 34 97 71 86 115 98 71 56 61 34 125]
Rust 示例:
use serde_json::{Result, Value};
use serde::Serialize;
#[derive(Serialize, Debug)]
pub struct Response {
pub body: ::std::vec::Vec<u8>,
}
fn main() {
let body_str = "hello".to_string();
let resp = Response {
body: Vec::from(body_str)
};
println!("resp: {:?}", resp);
let result = serde_json::to_vec(&resp).unwrap();
println!("result: {:?}",result)
}
Rust 结果:
resp: Response { body: [104, 101, 108, 108, 111] }
result: [123, 34, 98, 111, 100, 121, 34, 58, 91, 49, 48, 52, 44, 49, 48, 49, 44, 49, 48, 56, 44, 49, 48, 56, 44, 49, 49, 49, 93, 125]
英文:
The results I want are as follows, but the result of Rust is another.
[123 34 66 111 100 121 34 58 34 97 71 86 115 98 71 56 61 34 125]
golang sample:
type Response struct {
Body []byte
}
func Test_Marshal(t *testing.T) {
body := "hello"
resp := &Response{
Body:[]byte(body),
}
t.Log("resp:", resp)
result, _ := json.Marshal(resp)
t.Log("result: ", result)
}
go result:
aa_test.go:17: resp: &{[104 101 108 108 111]}
aa_test.go:19: result: [123 34 66 111 100 121 34 58 34 97 71 86 115 98 71 56 61 34 125]
rust sample:
use serde_json::{Result, Value};
use serde::Serialize;
#[derive(Serialize, Debug)]
pub struct Response {
pub body: ::std::vec::Vec<u8>,
}
fn main() {
let body_str = "hello".to_string();
let resp = Response {
body: Vec::from(body_str)
};
println!("resp: {:?}", resp);
let result = serde_json::to_vec(&resp).unwrap();
println!("result: {:?}",result)
}
rust result
resp: Response { body: [104, 101, 108, 108, 111] }
result: [123, 34, 98, 111, 100, 121, 34, 58, 91, 49, 48, 52, 44, 49, 48, 49, 44, 49, 48, 56, 44, 49, 48, 56, 44, 49, 49, 49, 93, 125]
答案1
得分: 5
如果我们将你的两个输出从字节数组转换为ASCII字符串(我使用了一个简单的JavaScript代码 - array.map(ch => String.fromCharCode(ch)).join("")
),我们将得到以下结果:
// for Rust
{"body":[104,101,108,108,111]}
// for Go
{"Body":"aGVsbG8="}
因此,有两个不同之处:
- 第一个也是最明显的区别是字段命名。两个序列化器都将字段名保留在JSON中,就像在源代码中一样,在Go中是
Body
(大写),而在Rust中是body
(小写)。 - 第二个区别是Rust将值按原样编码 - 即字节数组被视为数字的向量(它不会得到任何特殊处理)。然而,在Go中,它被转换为Base64字符串 - 这是一个明确记录的例外情况,可能是因为在大多数情况下这是预期的行为。
现在,要做什么取决于您想要使用的行为。我不知道如何更改Go的输出,所以我假设它是正确的,而Rust的输出需要进行调整。
- 要修复键,您可以简单地使用
serde::rename("Body")
注释您的字段。 - 要修复值,最简单的方法可能是使用
serde::serialize_with
属性和base64
crate - 可以在这里找到一个示例:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Response {
#[serde(with="base64")]
pub body: Vec<u8>,
}
mod base64 {
use serde::{Serialize, Deserialize};
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = base64::encode(v);
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64 = String::deserialize(d)?;
base64::decode(base64.as_bytes())
.map_err(|e| serde::de::Error::custom(e))
}
}
英文:
If we convert both your outputs from byte arrays to ASCII strings (I've used a simple Javascript code - array.map(ch => String.fromCharCode(ch)).join("")
), we'll get the following:
// for Rust
{"body":[104,101,108,108,111]}
// for Go
{"Body":"aGVsbG8="}
So, there seem to be two differences:
- The first and the most obvious is the fields naming. Both serializers leave the field name in JSON as it was in the source code, so in Go it is
Body
(capitalized), while in Rust it isbody
(lower-case). - The second is that Rust encodes the value as-is - i.e. the byte array is treated as a vector of numbers (it doesn't get any special treatment). In Go, however, it is converted to Base64 string - this is an explicitly documented exception, probably because in most cases this is the intended behavior.
Now, what to do here? It depends on what behavior you want to use. I don't know how to change output with Go, so I assume that it is correct, and the Rust one needs to be tweaked.
- To fix the key, you can simply annotate your field with
serde::rename("Body")
. - To fix the value, the simplest way is probably to use
serde::serialize_with
attribute together withbase64
crate - an example can be found here:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Response {
#[serde(with="base64")]
pub body: Vec<u8>,
}
mod base64 {
use serde::{Serialize, Deserialize};
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = base64::encode(v);
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64 = String::deserialize(d)?;
base64::decode(base64.as_bytes())
.map_err(|e| serde::de::Error::custom(e))
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论