如何在C#中用Protocol Buffer序列化含NetTopologySuite Polygon的City类?
解决NetTopologySuite Polygon类型的Protobuf序列化问题
方案一:使用protobuf-net自定义序列化器
protobuf-net默认不支持NetTopologySuite的Polygon类型,你可以通过**代理类(Surrogate)**实现自定义序列化逻辑:
- 定义Polygon的代理类,映射其可序列化的核心数据:
[ProtoContract] public classPolygonSurrogate { [ProtoMember(1)] public Coordinate[] Coordinates { get; set; } // 从NetTopologySuite Polygon转换为代理类 public static implicit operatorPolygonSurrogate(Polygon polygon) { if (polygon == null) return null; return newPolygonSurrogate { Coordinates = polygon.Coordinates.ToArray() }; } // 从代理类转换回NetTopologySuite Polygon public static implicit operatorPolygon(PolygonSurrogate surrogate) { if (surrogate == null) return null; return newPolygon(newLinearRing(surrogate.Coordinates)); } }
- 在应用启动时注册代理类,让protobuf-net识别Polygon类型的处理规则:
RuntimeTypeModel.Default.Add(typeof(Polygon), false).SetSurrogate(typeof(PolygonSurrogate));
完成注册后,你原来的City类即可正常序列化/反序列化,protobuf-net会自动通过代理类处理Polygon属性。
方案二:切换到Google.Protobuf实现
如果改用Google.Protobuf官方包,需遵循标准Protobuf流程,手动定义类型并处理转换:
- 编写
.proto文件定义数据结构:
syntax = "proto3"; message City { string id = 1; // Guid以字符串形式存储 string name = 2; int32 population = 3; Polygon area = 4; } message Polygon { repeated Coordinate coordinates = 1; } message Coordinate { double x = 1; double y = 2; optional double z = 3; // 对应Coordinate的Z值,可选 }
通过
Google.Protobuf.ToolsNuGet包生成C#类(或使用protoc工具)。编写类型转换扩展方法,实现NetTopologySuite类型与生成的Protobuf类型互转:
public static classPolygonConversionExtensions { // NetTopologySuite Polygon转Protobuf Polygon public static Proto.Polygon ToProtoPolygon(thisPolygon ntPolygon) { if (ntPolygon == null) return null; var protoPolygon = new Proto.Polygon(); foreach (var coord in ntPolygon.Coordinates) { var protoCoord = new Proto.Coordinate { X = coord.X, Y = coord.Y }; if (coord.HasZ) protoCoord.Z = coord.Z; protoPolygon.Coordinates.Add(protoCoord); } return protoPolygon; } // Protobuf Polygon转NetTopologySuite Polygon public staticPolygon ToNtPolygon(this Proto.Polygon protoPolygon) { if (protoPolygon == null) return null; var coordinates = protoPolygon.Coordinates .Select(c => newCoordinate(c.X, c.Y, c.Z.GetValueOrDefault(0))) .ToArray(); return newPolygon(newLinearRing(coordinates)); } }
- 序列化/反序列化时手动转换类型:
// 序列化示例 var city = new City { Id = Guid.NewGuid(), Name = "Beijing", Population = 21540000, Area = newPolygon(newLinearRing(new[] { /* 坐标点 */ })) }; var protoCity = new Proto.City { Id = city.Id.ToString(), Name = city.Name, Population = city.Population, Area = city.Area.ToProtoPolygon() }; byte[] serializedBytes; using (var stream = newMemoryStream()) { protoCity.WriteTo(stream); serializedBytes = stream.ToArray(); } // 反序列化示例 using (var stream = newMemoryStream(serializedBytes)) { var deserializedProtoCity = Proto.City.Parser.ParseFrom(stream); var deserializedCity = new City { Id = Guid.Parse(deserializedProtoCity.Id), Name = deserializedProtoCity.Name, Population = deserializedProtoCity.Population, Area = deserializedProtoCity.Area.ToNtPolygon() }; }
方案对比
- protobuf-net方案:无需额外定义.proto文件,代码侵入性低,适合希望保留原有类结构的场景。
- Google.Protobuf方案:符合Protobuf标准规范,跨语言兼容性更强,但需要手动处理类型转换,流程更繁琐。
内容的提问来源于stack exchange,提问作者goodstas
相关产品推荐
相关产品推荐

