Java和Rust之间的JSON序列化互转解决方案

我是蚂蚁背大象(Apache EventMesh PMC&Committer),文章对你有帮助给项目rocketmq-rust star,关注我GitHub:mxsm,文章有不正确的地方请您斧正,创建ISSUE提交PR~谢谢! Emal:mxsm@apache.com

1.背景

最近在使用Rust实现Rocketmq的项目 rocketmq-rust,在实现的过程中就会遇到请求头以及相关的数据JSON序列化在两个不同语言中的序列化和反序列的情况。在这种情况下遇到下面的问题:Java中存在继承,例如

typescript 复制代码
public class Test {

    public static void main(String[] args) {
        Student object = new Student();
        object.setName("mxsm");
        object.setAge("18");
        object.setSchool("杭州电子科技大学");
        System.out.println(JSON.toJSONString(object));
    }
    
    public static class Person {
        private String name;
        private String age;
		//ignore get set method
    }

    public static class Student extends Person {
        private String school;
		//ignore get set method
    }
}

那么在Java中序列化就会打印成这样:

shell 复制代码
{"age":"18","name":"mxsm","school":"杭州电子科技大学"}

问题就来了。在Rust中没有这样的继承关系该如何处理这样的代码情况。随着跨语言开发的流行,将数据在不同编程语言之间进行序列化和反序列化变得越来越重要。本文将探讨如何在Java和Rust之间实现JSON数据的序列化互转,并解决在Rust中处理Java继承序列化的挑战。

2. Java和Rust之间的JSON序列化互转

Java中有许多库可以用于JSON序列化,其中最流行的是Jackson和Gson以及Fastjson。我们将以Fastjson为例进行介绍(Rocketmq中使用的就是Fastjson进行序列)。Rust中使用serde 作为例子,在 Rocketmq-rust 项目中JSON序列化使用的是 serde 。

2.1 无继承的简单数据结构

Java示例代码:

java 复制代码
 public static class Person {
        private String name;
        private String age;
     	private String homeAddress
		//ignore get set method
 }

转换成对应的Rust代码

rust 复制代码
#[derive(Clone,Debug,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
pub struct Person{
    name: String,
    age: String,
    home_address: String
}

这里有个需要说明,在Java规范中字段的命名遵循的是驼峰,而rust使用的snake case ,两者是有区别的。 这里就需要注意如果是以Java为主的项目那么Rust的序列化和反序列化都应该使用驼峰的方式来进行。也就是上面代码的增加的 #[serde(rename_all="camelCase")] 而如果是Rust项目为主,很大可能序列后的JSON字符串使用的就是snake case 那么Java项目就需要进行相对应的处理。

2.2 Java代码有继承的结构

代码示例:

java 复制代码
public static class Person {
        private String name;
        private String age;
		//ignore get set method
    }

    public static class Student extends Person {
        private String school;
		//ignore get set method
    }

在Java代码中继承是很常见的一种操作,但是在Rust中struct的继承并不存在。那么这种情况下应该如何解决。

2.2.1 将所有的代码扁平化

代码扁平化这个是最简单的方式。以上面代码为例。如果扁平化后我们后我们处理后的Rust代码:

rust 复制代码
#[derive(Clone,Debug,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
pub struct Person{
    name: String,
    age: String,
}

#[derive(Clone,Debug,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
pub struct Person{
    name: String,
    age: String,
    school: String
}

所谓扁平化就是直接将继承代码的属性移到最底层的继承代码中。这样做就是简单,能够实现和Java相同的序列化的效果。

优点:

  • 简单,快速解决问题

缺点:

  • 需要实现的重复代码量大

2.2.2 抽象出来trait

将公共的方法操作方法抽象出来trait然后,使用trait继承的方式来实现。同样已上面的代码为例:

rust 复制代码
pub trait PersonTrait{
    fn get_name(&self)->String;
    fn get_age(&self)->String;
}

然后所有的struct进行实现。这个优缺点也很明显。

优点:

  • 简单,相比第一种方式多了一个抽象trait的步骤,其他的和第一种差不多
  • 可以使用Trait作为方法的参数或者struct的属性。提供了类似Java的多态(这个对于无需要进行序列化的比较好)

缺点:

  • 需要实现的重复代码量大

2.2.3 使用struct聚合配合serde的扁平化(重点)

使用struct聚合配合serde的扁平化来解决Java项目和Rust项目的JSON数据的序列化和反序列交互比较优的解。通用用上面的代码为例子。如果使用这种方式来处理,转换成Rust代码后的代码:

rust 复制代码
#[derive(Clone,Debug,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
pub struct Person{
    name: String,
    age: String,
}

#[derive(Clone,Debug,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
pub struct Person{
    #[serde(flatten)]  // 这个是扁平化关键
    person: Persion
    school: String
}

同样定义跟Java相同的代码,然后通过聚合的方式来模拟Rust中的继承关系。 将需要扁平化的代码进行扁平化使用 #[serde(flatten)] 来实现。

优点:

  • 能够和其他语言相同的JSON序列化方式(需要serde的扁平化支持)
  • 整体的序列化相对较灵活

缺点:

  • 需要Rust JSON序列化工具的支持。如果工具不支持没办法实现。只能手动序列化

说明:使用struct聚合配合serde的扁平化来解决JSON序列化的方式也是 Rocketmq-rust 项目中的解决方式。因为rocketmq-rust需要支持和Java版本进行互通。

3.总结

对于Java JSON序列化和Rust JSON序列化主要差异由以下两种原因引起:

  • 属性的命名规范,Java使用的是驼峰 而Rust使用的是snake case。这就导致在序列化的情况下两者之间的序列化会存在属性的命名差异。
  • Java有继承关系,继承的最底层的类进行序列化会将父类的属性进行扁平化序列化。而Rust中不存在所以导致Rust中的序列化的差异性,为了解决这种差异性就有了上面的三种方式。

就单纯解决JSON序列化使用 使用struct聚合配合serde的扁平化 是一种比较忧的解决方式。但是不是唯一的方式。

相关推荐
小曲程序4 分钟前
vue3 封装request请求
java·前端·typescript·vue
凡人的AI工具箱19 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
陈王卜22 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、22 分钟前
Spring Boot 注解
java·spring boot
java亮小白199727 分钟前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF34 分钟前
java Queue 详解
java·队列
VertexGeek38 分钟前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
2301_8112743144 分钟前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康1 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql