You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Java中使用Protobuf实现自定义Java类型的序列化与反序列化

Protocol Buffer自定义类型序列化/反序列化方案(JTS Geometry场景)

注:你的Proto定义中end_locaion字段号与start_locaion重复(都是3),会导致编译错误,建议修改为4:

syntax = "proto3";

import "google/protobuf/timestamp.proto";

message UberDrive {
  string user_id = 1;
  google.protobuf.Timestamp transaction_timestamp = 2;
  string start_locaion = 3; //WKT data
  string end_locaion = 4; //WKT data
}

针对你希望在POJO中直接使用org.locationtech.jts.geom.Geometry类型、自动处理WKT与Geometry转换的需求,以下是几种可行方案:

方案1:MapStruct自动映射(简单易落地)

利用MapStruct的自动代码生成能力,定义映射规则后自动完成Proto消息与业务POJO的转换,无需手动编写重复的序列化/反序列化逻辑。

步骤:

  1. 引入MapStruct依赖(以Maven为例)
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>
  1. 定义业务POJO(直接使用Geometry类型)
import org.locationtech.jts.geom.Geometry;
import java.time.Instant;

public class UberDrivePOJO {
    private String userId;
    private Instant transactionTimestamp;
    private Geometry startLocation;
    private Geometry endLocation;

    // 生成getter/setter
}
  1. 定义MapStruct映射接口,嵌入WKT与Geometry的转换逻辑
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UberDriveMapper {
    UberDriveMapper INSTANCE = Mappers.getMapper(UberDriveMapper.class);

    // Proto消息转业务POJO:WKT字符串转Geometry
    @Mapping(source = "startLocaion", target = "startLocation", qualifiedByName = "wktToGeometry")
    @Mapping(source = "endLocaion", target = "endLocation", qualifiedByName = "wktToGeometry")
    UberDrivePOJO protoToPojo(UberDrive proto);

    // 业务POJO转Proto消息:Geometry转WKT字符串
    @Mapping(source = "startLocation", target = "startLocaion", qualifiedByName = "geometryToWkt")
    @Mapping(source = "endLocation", target = "endLocaion", qualifiedByName = "geometryToWkt")
    UberDrive pojoToProto(UberDrivePOJO pojo);

    // 自定义WKT转Geometry方法
    @Named("wktToGeometry")
    default Geometry wktToGeometry(String wkt) {
        if (wkt == null || wkt.isBlank()) {
            return null;
        }
        try {
            return new WKTReader().read(wkt);
        } catch (Exception e) {
            throw new IllegalArgumentException("无效的WKT格式: " + wkt, e);
        }
    }

    // 自定义Geometry转WKT方法
    @Named("geometryToWkt")
    default String geometryToWkt(Geometry geometry) {
        if (geometry == null) {
            return null;
        }
        return new WKTWriter().write(geometry);
    }
}
  1. 实际使用
// Proto转POJO
UberDrive protoMsg = UberDrive.newBuilder().setStartLocaion("POINT(116.4038739 39.914885)").build();
UberDrivePOJO pojo = UberDriveMapper.INSTANCE.protoToPojo(protoMsg);

// POJO转Proto
UberDrivePOJO pojo = new UberDrivePOJO();
pojo.setStartLocation(new WKTReader().read("POINT(116.4038739 39.914885)"));
UberDrive protoMsg = UberDriveMapper.INSTANCE.pojoToProto(pojo);

方案2:自定义Protobuf代码生成插件(深度定制)

如果希望直接让Protobuf生成的Java类就包含Geometry字段(而非string),可以编写protoc的Java代码生成插件,识别带有//WKT data注释的string字段,生成时自动替换为Geometry类型,并内置WKT序列化/反序列化逻辑。

核心思路:

  • 继承com.google.protobuf.compiler.PluginProtos.CodeGenerator接口,实现自定义代码生成逻辑
  • 解析Proto文件的AST,筛选出标记为WKT的string字段
  • 生成对应的Geometry字段,并添加从WKT字符串转换的代码(调用WKTReader/WKTWriter)
  • 编译插件后,配置protoc使用该插件生成定制化的Java类

此方案需要熟悉Protobuf代码生成API,实现成本较高,但无需额外的映射层,直接使用生成的类即可。

方案3:Jackson/Gson TypeAdapter(适用于JSON序列化场景)

如果项目中使用JSON作为中间格式,可以自定义TypeAdapter,让序列化框架自动处理Geometry与WKT的转换。

以Gson为例:

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;

import java.io.IOException;

public class GeometryTypeAdapter extends TypeAdapter<Geometry> {
    private final WKTReader wktReader = new WKTReader();
    private final WKTWriter wktWriter = new WKTWriter();

    @Override
    public void write(JsonWriter out, Geometry value) throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        out.value(wktWriter.write(value));
    }

    @Override
    public Geometry read(JsonReader in) throws IOException {
        String wkt = in.nextString();
        if (wkt == null || wkt.isBlank()) {
            return null;
        }
        try {
            return wktReader.read(wkt);
        } catch (Exception e) {
            throw new IOException("解析WKT失败: " + wkt, e);
        }
    }
}

注册后即可直接序列化/反序列化带有Geometry字段的POJO:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Geometry.class, new GeometryTypeAdapter())
        .create();

// POJO转JSON
String json = gson.toJson(pojo);

// JSON转POJO
UberDrivePOJO pojo = gson.fromJson(json, UberDrivePOJO.class);

内容的提问来源于stack exchange,提问作者Elad Barkay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 11:43:20