I have 2 applications using protobuf in between. First one (C#) using protobuf-net with serializable object VisualSettings:
[ProtoContract]
public class VisualSettings
{
[ProtoContract]
public class BodyPartHolder
{
[ProtoMember(1)]
public BodyPartType type;
[ProtoMember(2)]
public int id;
public BodyPartHolder() { }
public BodyPartHolder(BodyPartType type, int id)
{
this.type = type;
this.id = id;
}
public BodyPartHolder Copy()
{
return new BodyPartHolder(type, id);
}
}
public interface IPaintHolder
{
int id { get; set; }
}
public interface ICustomPaintHolder
{
HSBColor hsb { get; set; }
}
[ProtoContract]
[ProtoInclude(100, typeof(BaseBodyPaintHolder)), ProtoInclude(200, typeof(BaseWheelPaintHolder)), ProtoInclude(300, typeof(BaseGlassPaintHolder))]
[ProtoInclude(400, typeof(BaseSmokePaintHolder)), ProtoInclude(500, typeof(BaseSuspensionPaintHolder)), ProtoInclude(600, typeof(BaseInteriorPaintHolder))]
public abstract class BasePaintHolder
{
[JsonIgnore]
public bool isCustom => this is ICustomPaintHolder;
[JsonIgnore]
public string materialName => CarPaintMaterials.GetName(material);
[JsonIgnore]
public CarPaintType material => materials[materialId];
[ProtoMember(1)]
public int materialId;
protected abstract CarPaintType[] materials { get; }
public BasePaintHolder() { }
public BasePaintHolder(int materialId)
{
this.materialId = materialId;
}
public virtual float GetCategoryCostCoefficient(CarSharedMeta carMeta)
{
return BasePaintItem.DefaultCostCoefficient;
}
public float GetMaterialCostCoefficient(CarSharedMeta carMeta)
{
return carMeta.GetPaintMaterialCostCoefficient(material);
}
public abstract bool Equals(BasePaintHolder other);
}
[ProtoContract]
[ProtoInclude(100, typeof(BodyPaintHolder)), ProtoInclude(200, typeof(BodyCustomPaintHolder))]
public abstract class BaseBodyPaintHolder : BasePaintHolder
{
[ProtoMember(1)]
public BodyPartType part;
protected override CarPaintType[] materials => CarPaintMaterials.Body;
public BaseBodyPaintHolder() { }
public BaseBodyPaintHolder(BodyPartType part, int materialId) : base(materialId)
{
this.part = part;
}
public override float GetCategoryCostCoefficient(CarSharedMeta carMeta)
{
return carMeta.GetPaintCategoryCostCoefficient(part);
}
public abstract BaseBodyPaintHolder Copy();
}
[ProtoContract]
public class BodyPaintHolder : BaseBodyPaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public BodyPaintHolder() { }
public BodyPaintHolder(BodyPartType part, int materialId, int id) : base(part, materialId)
{
this.id = id;
}
public override BaseBodyPaintHolder Copy()
{
return new BodyPaintHolder(part, materialId, id);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as BodyPaintHolder;
return (otherPaint != null) && (otherPaint.part == part) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class BodyCustomPaintHolder : BaseBodyPaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public BodyCustomPaintHolder() { }
public BodyCustomPaintHolder(BodyPartType part, int materialId, HSBColor hsb) : base(part, materialId)
{
this.hsb = hsb;
}
public override BaseBodyPaintHolder Copy()
{
return new BodyCustomPaintHolder(part, materialId, hsb);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as BodyCustomPaintHolder;
return (otherPaint != null) && (otherPaint.part == part) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoContract]
[ProtoInclude(100, typeof(WheelPaintHolder)), ProtoInclude(200, typeof(WheelCustomPaintHolder))]
public abstract class BaseWheelPaintHolder : BasePaintHolder
{
[ProtoMember(1)]
public WheelPaintPart part;
protected override CarPaintType[] materials => CarPaintMaterials.Wheel;
public BaseWheelPaintHolder() { }
public BaseWheelPaintHolder(WheelPaintPart part, int materialId) : base(materialId)
{
this.part = part;
}
public abstract BaseWheelPaintHolder Copy();
}
[ProtoContract]
public class WheelPaintHolder : BaseWheelPaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public WheelPaintHolder() { }
public WheelPaintHolder(WheelPaintPart part, int materialId, int id) : base(part, materialId)
{
this.id = id;
}
public override BaseWheelPaintHolder Copy()
{
return new WheelPaintHolder(part, materialId, id);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as WheelPaintHolder;
return (otherPaint != null) && (otherPaint.part == part) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class WheelCustomPaintHolder : BaseWheelPaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public WheelCustomPaintHolder() { }
public WheelCustomPaintHolder(WheelPaintPart part, int materialId, HSBColor hsb) : base(part, materialId)
{
this.hsb = hsb;
}
public override BaseWheelPaintHolder Copy()
{
return new WheelCustomPaintHolder(part, materialId, hsb);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as WheelCustomPaintHolder;
return (otherPaint != null) && (otherPaint.part == part) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoContract]
[ProtoInclude(100, typeof(GlassPaintHolder)), ProtoInclude(200, typeof(GlassCustomPaintHolder))]
public abstract class BaseGlassPaintHolder : BasePaintHolder
{
[ProtoMember(1)]
public GlassPaintPart part { get; set; }
protected override CarPaintType[] materials => CarPaintMaterials.Glass;
public BaseGlassPaintHolder() { }
public BaseGlassPaintHolder(GlassPaintPart part, int materialId) : base(materialId)
{
this.part = part;
}
public abstract BaseGlassPaintHolder Copy();
}
[ProtoContract]
public class GlassPaintHolder : BaseGlassPaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public GlassPaintHolder() { }
public GlassPaintHolder(GlassPaintPart part, int materialId, int id) : base(part, materialId)
{
this.id = id;
}
public override BaseGlassPaintHolder Copy()
{
return new GlassPaintHolder(part, materialId, id);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as GlassPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class GlassCustomPaintHolder : BaseGlassPaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public GlassCustomPaintHolder() { }
public GlassCustomPaintHolder(GlassPaintPart part, int materialId, HSBColor hsb) : base(part, materialId)
{
this.hsb = hsb;
}
public override BaseGlassPaintHolder Copy()
{
return new GlassCustomPaintHolder(part, materialId, hsb);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as GlassCustomPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoContract]
[ProtoInclude(100, typeof(SmokePaintHolder)), ProtoInclude(200, typeof(SmokeCustomPaintHolder))]
public abstract class BaseSmokePaintHolder : BasePaintHolder
{
protected override CarPaintType[] materials => CarPaintMaterials.Smoke;
public BaseSmokePaintHolder() { }
public BaseSmokePaintHolder(int materialId) : base(materialId) { }
public abstract BaseSmokePaintHolder Copy();
}
[ProtoContract]
public class SmokePaintHolder : BaseSmokePaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public SmokePaintHolder() { }
public SmokePaintHolder(int materialId, int id) : base(materialId)
{
this.id = id;
}
public override BaseSmokePaintHolder Copy()
{
return new SmokePaintHolder(materialId, id);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as SmokePaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class SmokeCustomPaintHolder : BaseSmokePaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public SmokeCustomPaintHolder() { }
public SmokeCustomPaintHolder(int materialId, HSBColor hsb) : base(materialId)
{
this.hsb = hsb;
}
public override BaseSmokePaintHolder Copy()
{
return new SmokeCustomPaintHolder(materialId, hsb);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as SmokeCustomPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoContract]
[ProtoInclude(100, typeof(SuspensionPaintHolder)), ProtoInclude(200, typeof(SuspensionCustomPaintHolder))]
public abstract class BaseSuspensionPaintHolder : BasePaintHolder
{
[ProtoMember(1)]
public SuspensionPaintPart part { get; set; }
protected override CarPaintType[] materials => CarPaintMaterials.Suspension;
public BaseSuspensionPaintHolder() { }
public BaseSuspensionPaintHolder(SuspensionPaintPart part, int materialId) : base(materialId)
{
this.part = part;
}
public override float GetCategoryCostCoefficient(CarSharedMeta carMeta)
{
return carMeta.GetPaintCategoryCostCoefficient(BodyPartType.Suspension);
}
public abstract BaseSuspensionPaintHolder Copy();
}
[ProtoContract]
public class SuspensionPaintHolder : BaseSuspensionPaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public SuspensionPaintHolder() { }
public SuspensionPaintHolder(SuspensionPaintPart part, int id, int materialId) : base(part, materialId)
{
this.id = id;
}
public override BaseSuspensionPaintHolder Copy()
{
return new SuspensionPaintHolder(part, id, materialId);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as SuspensionPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class SuspensionCustomPaintHolder : BaseSuspensionPaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public SuspensionCustomPaintHolder() { }
public SuspensionCustomPaintHolder(SuspensionPaintPart part, HSBColor hsb, int materialId) : base(part, materialId)
{
this.hsb = hsb;
}
public override BaseSuspensionPaintHolder Copy()
{
return new SuspensionCustomPaintHolder(part, hsb, materialId);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as SuspensionCustomPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoContract]
[ProtoInclude(100, typeof(InteriorPaintHolder)), ProtoInclude(200, typeof(InteriorCustomPaintHolder))]
public abstract class BaseInteriorPaintHolder : BasePaintHolder
{
[ProtoMember(1)]
public InteriorPaintPart part { get; set; }
protected override CarPaintType[] materials => CarPaintMaterials.Interior;
public BaseInteriorPaintHolder() { }
public BaseInteriorPaintHolder(InteriorPaintPart part, int materialId) : base(materialId)
{
this.part = part;
}
public override float GetCategoryCostCoefficient(CarSharedMeta carMeta)
{
return carMeta.GetPaintCategoryCostCoefficient(BodyPartType.SeatLeft);
}
public abstract BaseInteriorPaintHolder Copy();
}
[ProtoContract]
public class InteriorPaintHolder : BaseInteriorPaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public InteriorPaintHolder() { }
public InteriorPaintHolder(InteriorPaintPart part, int id, int materialId) : base(part, materialId)
{
this.id = id;
}
public override BaseInteriorPaintHolder Copy()
{
return new InteriorPaintHolder(part, id, materialId);
}
public override bool Equals(BasePaintHolder other)
{
return (other is InteriorPaintHolder otherPaint) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class InteriorCustomPaintHolder : BaseInteriorPaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public InteriorCustomPaintHolder() { }
public InteriorCustomPaintHolder(InteriorPaintPart part, HSBColor hsb, int materialId) : base(part, materialId)
{
this.hsb = hsb;
}
public override BaseInteriorPaintHolder Copy()
{
return new InteriorCustomPaintHolder(part, hsb, materialId);
}
public override bool Equals(BasePaintHolder other)
{
return (other is InteriorCustomPaintHolder otherPaint) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoContract]
[ProtoInclude(100, typeof(GenericPaintHolder)), ProtoInclude(200, typeof(GenericCustomPaintHolder))]
public abstract class BaseGenericPaintHolder : BasePaintHolder
{
protected override CarPaintType[] materials => CarPaintMaterials.Generic;
public BaseGenericPaintHolder() { }
public BaseGenericPaintHolder(int materialId) : base(materialId) { }
public abstract BaseGenericPaintHolder Copy();
}
[ProtoContract]
public class GenericPaintHolder : BaseGenericPaintHolder, IPaintHolder
{
[ProtoMember(1)]
public int id { get; set; }
public GenericPaintHolder() { }
public GenericPaintHolder(int materialId, int id) : base(materialId)
{
this.id = id;
}
public override BaseGenericPaintHolder Copy()
{
return new GenericPaintHolder(materialId, id);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as GenericPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && (otherPaint.id == id);
}
}
[ProtoContract]
public class GenericCustomPaintHolder : BaseGenericPaintHolder, ICustomPaintHolder
{
[ProtoMember(1)]
public HSBColor hsb { get; set; }
public GenericCustomPaintHolder() { }
public GenericCustomPaintHolder(int materialId, HSBColor hsb) : base(materialId)
{
this.hsb = hsb;
}
public override BaseGenericPaintHolder Copy()
{
return new GenericCustomPaintHolder(materialId, hsb);
}
public override bool Equals(BasePaintHolder other)
{
var otherPaint = other as GenericCustomPaintHolder;
return (otherPaint != null) && (otherPaint.materialId == materialId) && HSBColor.EqualsOpaque(otherPaint.hsb, hsb);
}
}
[ProtoMember(1)]
public List<BaseBodyPaintHolder> bodyPaints = null;
[ProtoMember(2)]
public List<BaseWheelPaintHolder> frontWheelPaints = null;
[ProtoMember(3)]
public List<BaseWheelPaintHolder> rearWheelPaints = null;
[ProtoMember(4)]
public List<BaseSuspensionPaintHolder> suspensionPaints = null;
[ProtoMember(5)]
public BaseGlassPaintHolder glassPaint = null;
[ProtoMember(6)]
public BaseSmokePaintHolder smokePaint = null;
[ProtoMember(14)]
public BaseGenericPaintHolder genericPaint = null;
[ProtoMember(15)]
public List<BaseInteriorPaintHolder> interiorPaints = null;
[ProtoMember(16)]
public List<BaseGlassPaintHolder> lightGlassesPaints = null;
[ProtoMember(7)]
public Dictionary<WheelAxles, int> wheelTires = null;
[ProtoMember(8)]
public List<BodyPartHolder> bodyParts = null;
[ProtoMember(9)]
public int frontRimPartId = 1;
[ProtoMember(10)]
public int rearRimPartId = 1;
[ProtoMember(11)]
public int bodyKitId;
[ProtoMember(12)]
public List<VinylLayer> vinylLayers = null;
[ProtoMember(13)]
public string workshopItemId = null;
public VisualSettings()
{
bodyPaints = new List<BaseBodyPaintHolder>();
rearWheelPaints = new List<BaseWheelPaintHolder>();
frontWheelPaints = new List<BaseWheelPaintHolder>();
suspensionPaints = new List<BaseSuspensionPaintHolder>();
interiorPaints = new List<BaseInteriorPaintHolder>();
lightGlassesPaints = new List<BaseGlassPaintHolder>();
bodyParts = new List<BodyPartHolder>();
vinylLayers = new List<VinylLayer>();
wheelTires = new Dictionary<WheelAxles, int>();
}
public VisualSettings(VisualSettings copyFrom)
{
bodyPaints = new List<BaseBodyPaintHolder>(copyFrom.bodyPaints);
rearWheelPaints = new List<BaseWheelPaintHolder>(copyFrom.rearWheelPaints);
frontWheelPaints = new List<BaseWheelPaintHolder>(copyFrom.frontWheelPaints);
suspensionPaints = new List<BaseSuspensionPaintHolder>(copyFrom.suspensionPaints);
interiorPaints = new List<BaseInteriorPaintHolder>(copyFrom.interiorPaints);
lightGlassesPaints = new List<BaseGlassPaintHolder>(copyFrom.lightGlassesPaints);
bodyParts = new List<BodyPartHolder>(copyFrom.bodyParts);
vinylLayers = new List<VinylLayer>(copyFrom.vinylLayers);
wheelTires = new Dictionary<WheelAxles, int>(copyFrom.wheelTires);
glassPaint = copyFrom.glassPaint;
smokePaint = copyFrom.smokePaint;
genericPaint = copyFrom.genericPaint;
frontRimPartId = copyFrom.frontRimPartId;
rearRimPartId = copyFrom.rearRimPartId;
bodyKitId = copyFrom.bodyKitId;
workshopItemId = copyFrom.workshopItemId;
}
}
I generated .proto file via Serializer.GetProto<VisualSettings>()
and generate Java classes (for second one app) with using that .proto file.
Proto file genereted by protobuf-net:
syntax = "proto2";
package visual_settings;
option java_multiple_files = true;
option java_package = "com.test";
option java_outer_classname = "VisualSettingsProto";
message BaseBodyPaintHolder {
optional BodyPartType part = 1 [default = Chassis];
// the following represent sub-types; at most 1 should have a value
optional BodyPaintHolder BodyPaintHolder = 100;
optional BodyCustomPaintHolder BodyCustomPaintHolder = 200;
}
message BaseGenericPaintHolder {
// the following represent sub-types; at most 1 should have a value
optional GenericPaintHolder GenericPaintHolder = 100;
optional GenericCustomPaintHolder GenericCustomPaintHolder = 200;
}
message BaseGlassPaintHolder {
optional GlassPaintPart part = 1 [default = Glasses];
// the following represent sub-types; at most 1 should have a value
optional GlassPaintHolder GlassPaintHolder = 100;
optional GlassCustomPaintHolder GlassCustomPaintHolder = 200;
}
message BaseInteriorPaintHolder {
optional InteriorPaintPart part = 1 [default = SteeringWheelAlcantar];
// the following represent sub-types; at most 1 should have a value
optional InteriorPaintHolder InteriorPaintHolder = 100;
optional InteriorCustomPaintHolder InteriorCustomPaintHolder = 200;
}
message BasePaintHolder {
optional int32 materialId = 1 [default = 0];
// the following represent sub-types; at most 1 should have a value
optional BaseBodyPaintHolder BaseBodyPaintHolder = 100;
optional BaseWheelPaintHolder BaseWheelPaintHolder = 200;
optional BaseGlassPaintHolder BaseGlassPaintHolder = 300;
optional BaseSmokePaintHolder BaseSmokePaintHolder = 400;
optional BaseSuspensionPaintHolder BaseSuspensionPaintHolder = 500;
optional BaseInteriorPaintHolder BaseInteriorPaintHolder = 600;
}
message BaseSmokePaintHolder {
// the following represent sub-types; at most 1 should have a value
optional SmokePaintHolder SmokePaintHolder = 100;
optional SmokeCustomPaintHolder SmokeCustomPaintHolder = 200;
}
message BaseSuspensionPaintHolder {
optional SuspensionPaintPart part = 1 [default = Arm];
// the following represent sub-types; at most 1 should have a value
optional SuspensionPaintHolder SuspensionPaintHolder = 100;
optional SuspensionCustomPaintHolder SuspensionCustomPaintHolder = 200;
}
message BaseWheelPaintHolder {
optional WheelPaintPart part = 1 [default = SpokeFront];
// the following represent sub-types; at most 1 should have a value
optional WheelPaintHolder WheelPaintHolder = 100;
optional WheelCustomPaintHolder WheelCustomPaintHolder = 200;
}
message BodyCustomPaintHolder {
optional HSBColor hsb = 1;
}
message BodyPaintHolder {
optional int32 id = 1 [default = 0];
}
message BodyPartHolder {
optional BodyPartType type = 1 [default = Chassis];
optional int32 id = 2 [default = 0];
}
enum BodyPartType {
Chassis = 0;
BumperFront = 1;
BumperRear = 2;
Skirts = 3;
Doors = 4;
Roof = 5;
Mirrors = 6;
Bonnet = 7;
Trunk = 8;
Spoiler = 9;
LightsFront = 10;
LightsRear = 11;
Exhaust = 12;
Cage = 13;
Suspension = 14;
Interior = 15;
SeatLeft = 16;
SeatRight = 17;
SteeringWheel = 18;
Handbrake = 19;
Shifter = 20;
Dashboard = 21;
Engine = 22;
BlobShadow = 23;
}
message GenericCustomPaintHolder {
optional HSBColor hsb = 1;
}
message GenericPaintHolder {
optional int32 id = 1 [default = 0];
}
message GlassCustomPaintHolder {
optional HSBColor hsb = 1;
}
message GlassPaintHolder {
optional int32 id = 1 [default = 0];
}
enum GlassPaintPart {
Glasses = 0;
FrontLight = 1;
RearLight = 2;
FogLight = 3;
}
message HSBColor {
optional float h = 1 [default = 0];
optional float s = 2 [default = 0];
optional float b = 3 [default = 0];
optional float a = 4 [default = 0];
}
message InteriorCustomPaintHolder {
optional HSBColor hsb = 1;
}
message InteriorPaintHolder {
optional int32 id = 1 [default = 0];
}
enum InteriorPaintPart {
SteeringWheelAlcantar = 0;
SteeringWheelStrings = 1;
SteeringWheelSpokes = 2;
LeftSeatBase = 3;
LeftSeatBottom = 4;
RightSeatBase = 5;
RightSeatBottom = 6;
GearBoxHandle = 7;
HandbrakeHandle = 8;
}
message KeyValuePair_WheelAxles_Int32 {
optional WheelAxles Key = 1;
optional int32 Value = 2;
}
message SmokeCustomPaintHolder {
optional HSBColor hsb = 1;
}
message SmokePaintHolder {
optional int32 id = 1 [default = 0];
}
message SuspensionCustomPaintHolder {
optional HSBColor hsb = 1;
}
message SuspensionPaintHolder {
optional int32 id = 1 [default = 0];
}
enum SuspensionPaintPart {
Arm = 0;
Rack = 1;
Spring = 2;
BrakeCaliper = 3;
}
message VinylLayerSurrogate {
optional int32 rawId = 1 [default = 0];
optional float angle = 2 [default = 0];
optional bool isSymmetry = 3 [default = false];
optional bool isHorizontalFlip = 4 [default = false];
optional bool isVerticalFlip = 5 [default = false];
optional bool isPassThrough = 6 [default = false];
optional bytes colors = 7;
optional bytes transform = 8;
optional int32 groupMask = 9 [default = 0];
optional float clipMaskShift = 10 [default = 0];
}
message VisualSettings {
repeated BasePaintHolder bodyPaints = 1;
repeated BasePaintHolder frontWheelPaints = 2;
repeated BasePaintHolder rearWheelPaints = 3;
repeated BasePaintHolder suspensionPaints = 4;
optional BasePaintHolder glassPaint = 5;
optional BasePaintHolder smokePaint = 6;
repeated KeyValuePair_WheelAxles_Int32 wheelTires = 7;
repeated BodyPartHolder bodyParts = 8;
optional int32 frontRimPartId = 9 [default = 0];
optional int32 rearRimPartId = 10 [default = 0];
optional int32 bodyKitId = 11 [default = 0];
repeated VinylLayerSurrogate vinylLayers = 12;
optional string workshopItemId = 13;
optional BaseGenericPaintHolder genericPaint = 14;
repeated BasePaintHolder interiorPaints = 15;
repeated BasePaintHolder lightGlassesPaints = 16;
}
enum WheelAxles {
Front = 0;
Rear = 1;
}
message WheelCustomPaintHolder {
optional HSBColor hsb = 1;
}
message WheelPaintHolder {
optional int32 id = 1 [default = 0];
}
enum WheelPaintPart {
SpokeFront = 0;
SpokeRear = 1;
RimFront = 2;
RimRear = 3;
}
With generated java classes I can deserialize data from C# without problems, but when I'm serialising data (VisualSetting) on Java side, C# couldn't deserialize it, I'm receiving the error:
ProtoException: No parameterless constructor found for BasePaintHolder
.
But java can deserialize own serialized data.
I'm suspecting, what protobuf-net generated .proto template not in right way. Could I improve my .proto file? Or I'm doing something wrong?
CodePudding user response:
This is happening because of the inheritance, basically; inheritance isn't a concept that protobuf directly supports, so protobuf-net models inheritance via nested sub-objects; to avoid some complexity, when serializing this, protobuf-net always starts by writing the inheritance parts first - so: when serializing BasePaintHolder
, it might serialize field 400 (the sub-type data for BaseSmokePaintHolder
), then field 1 (materialId
). This means that when it comes to deserialize, it has an obvious construction path, i.e. by the time it gets field data, it has already constructed the final concrete type.
This is fine, for protobuf-net to protobuf-net scenarios, but when we work with the same data in Java, it doesn't know or care about this, and elects to serialize in ascending field order, i.e. field 1, then field 400. It is explicit in the specification that fields may be out of order, but Java can choose to do whatever it wants.
So: now we take that data back to protobuf-net, and we see field 1 first; at that point, we have no clue what the final type will be, and nowhere (yet) to stick the data. With no better options, the library tries to use the base type BasePaintHolder
as a placeholder until it can make better decisions, but it turns out that this type is abstract
, so: even that fails.
So: that's the why, and some back-story. What can we do?
Well, the first thing we could try might be to remove the abstract
, making it technically constructable. To prevent external code accidentally creating instances, you might also make the constructors protected
, internal
, or private protected
(which means the intersection of protected
and internal
). We could also try using the SkipConstructor=true
modifier on [ProtoContract]
.
If that doesn't work: it might be necessary to basically work with 2 different object models; one that is simple - essentially more like the Java version, perhaps by running your schema through https://protogen.marcgravell.com/ - and one that has the inheritance; use the simple version for deserialization (and optionally serialization, although that shouldn't matter), and then re-map the data in your own code to the actual model. The other option is for me to figure out a way of doing all this magically and automatically; which: isn't a small job.