Nested Types in C# generated code causes serilization issue

111 views
Skip to first unread message

arun kumar

unread,
Aug 19, 2019, 8:33:20 AM8/19/19
to Protocol Buffers
All,

I am new to the usage of proto buffer with C sharp.. All I try is to get the input in XML and convert to C# object and then using google protobuf to serialize and deserialize. 

While converting the xml input to C# object, i am getting below because of nested types 

--------------------------------

Unhandled Exception: System.InvalidOperationException: There was an error reflecting type 'SCL.PC.ClientIdentifier'. ---> System.InvalidOperationException: SCL.PC.ClientIdentifier+Types cannot be serialized. Static types cannot be used as parameters or return types.

----------------------------------

It complains Types cannot be serialized. Please help me out 

Marc Gravell

unread,
Aug 19, 2019, 10:26:45 AM8/19/19
to Protocol Buffers
And can we see some code that actually demonstrates this problem? That would really help here.

arun kumar

unread,
Aug 20, 2019, 1:32:04 AM8/20/19
to Protocol Buffers
@Marc Gravell,

I auto-generated ".cs" files from each ".proto" file. . Whereever a message declared inside another message in proto, auto-generated cs file is generated as " Nested Types " and Types class is generated as static. (I guess , its how the generator defined)

And, I am trying to convert my xml data into CS object using XMLSerializer , it complains 

There was an error reflecting type 'SCL.PC.ClientIdentifier'. ---> System.InvalidOperationException: SCL.PC.ClientIdentifier+Types cannot be serialized. Static types cannot be used as parameters or return types.

All I am trying to do is ,

Converting XML data --> CS Object (IMessage) --> proto buf Serialize into stream 


Is there any way, to convert the xml data to IMessageObject.. I can see MessageParser has ParseJson object.  Likewise, anything available for xml ? 
 

   var serializer = new XmlSerializer(typeof(SCL.PC.ClientIdentifier));

Auto-Generated CS File :

public sealed partial class ClientIdentifier : pb::IMessage<ClientIdentifier> {
    private static readonly pb::MessageParser<ClientIdentifier> _parser = new pb::MessageParser<ClientIdentifier>(() => new ClientIdentifier());
    private pb::UnknownFieldSet _unknownFields;
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
    public static pb::MessageParser<ClientIdentifier> Parser { get { return _parser; } }

---

    #region Nested types
    /// <summary>Container for nested types declared in the ClientIdentifier message type.</summary>
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
    public static partial class Types {
      public enum Context {
        /// <summary>
        /// The client's identifier consists of a functional identifier (FID) in big-endian format.  Valid values range from 0x00000000 to 0xFFFFFFFF.
        /// </summary>
        [pbr::OriginalName("CFID")] Cfid = 0,
        /// <summary>
        /// The client's identifier consists of an ASCII-encoded serial number.
        /// </summary>
        [pbr::OriginalName("CSN")] Csn = 1,
      }

    }
    #endregion

  }

  #endregion


Marc Gravell

unread,
Aug 20, 2019, 2:50:52 PM8/20/19
to arun kumar, Protocol Buffers
Can you show any of your XmlSerializer code here, so can understand it? I wouldn't expect the presence of a static type to break XmlSerializer, I admit! But also: protobuf code doesn't usually guarantee anything other than that it should work with the corresponding protobuf library.

--
You received this message because you are subscribed to the Google Groups "Protocol Buffers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to protobuf+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/protobuf/a6a61e73-3253-4191-b2a0-27cb63d26bde%40googlegroups.com.

Arun

unread,
Aug 21, 2019, 2:05:58 AM8/21/19
to Marc Gravell, Protocol Buffers

I am not able to initialize XmlSerializer class itself. It is failing saying "static Types cannot be serialized "
--
-- Arun

Marc Gravell

unread,
Aug 21, 2019, 4:36:38 AM8/21/19
to Arun, Protocol Buffers
K, I took the tiny bit of code you posted, and added something that uses it with XmlSerializer, which basically meant implementing the interface (with NIEs) and adding:

using System;
using System.Xml.Serialization;
using pb = global::Google.Protobuf;
using pbr = global::Google.Protobuf.Reflection;

static class Program
{
    static void Main()
    {
        var ser = new XmlSerializer(typeof(ClientIdentifier));
        var obj = new ClientIdentifier();
        ser.Serialize(Console.Out, obj);
    }
}

---

And here's the thing: it works fine! So: if anyone is going to be able to help you here, you're going to need to meet us half way, by *showing us the code that doesn't work*.
--
Regards,

Marc

Arun

unread,
Aug 21, 2019, 5:28:45 AM8/21/19
to Marc Gravell, Protocol Buffers
I did the same but it throwing the error.. I dont know what I am making mistake here . All my application are 3.5 .net version, 

Please find  actual CS file which I am trying to serialize. 
--
-- Arun

SCLPCClientIdentifier.cs

Marc Gravell

unread,
Aug 21, 2019, 6:09:11 AM8/21/19
to Arun, Protocol Buffers
OK; that helps a bit; now, I've tried that with:

using Google.Protobuf;
using SCL.PC;
using System;
using System.Xml.Serialization;

static class P
{
    static void Main()
    {
        var obj = new ClientIdentifier
        {
            Context = ClientIdentifier.Types.Context.Cfid,
            Value = ByteString.CopyFrom(00, 01, 02, 03)
        };
        var ser = new XmlSerializer(obj.GetType());
        ser.Serialize(Console.Out, obj);
    }
}

---

I **cannot** reproduce the error you're seeing, but I *do* get another error, as shown below.

This tells me, ultimately, that your model *isn't going to work with XmlSerializer*. And that's fine - the code is only *tested* against the specific API it is designed for - Google's protobuf library in this case.

If XmlSerializer on some down-level version of .NET has *even more* problems, that's... unexpected, but not amazingly surprising.

If you want to use multiple serializers, usually you should have a serialization DTO model *per serializer* - one that is designed for use with that serializer.

However, there may be another option *if you want*. Google's generated code that is contributing to the pain here. Looking at your generated code, it looks like your schema is something like:

syntax = "proto3";
message ClientIdentifier {
  Context context = 1;
  bytes value = 2;
}
enum Context {
  Cfid = 0;
  Csn = 1;
}

Now, I can run that through protobuf-net's tooling (https://protogen.marcgravell.com/#g50e914251fc5d16b94cdc695c8ebac20) which gives me the simpler code (attached); if I then run that with the tweaked code:

static class P
{
    static void Main()
    {
        var obj = new ClientIdentifier
        {
            Context = Context.Cfid,
            Value = new byte[] { 00, 01, 02, 03 }
        };
        var ser = new XmlSerializer(obj.GetType());
        ser.Serialize(Console.Out, obj);
    }
}

it now all works fine in my version of .NET, and I'm *guessing* that it'll work in yours too. This is using a **different library**, specifically protobuf-net rather than Google.ProtoBuf, but: the key point here is that they both implement the same specification so should be interchangeable.

Up to you!

---

XmlSerializer exception.

Unhandled exception. System.InvalidOperationException: There was an error reflecting type 'SCL.PC.ClientIdentifier'.
 ---> System.InvalidOperationException: To be XML serializable, types which inherit from IEnumerable must have an implementation of Add(System.Object) at all levels of their inheritance hierarchy. Google.Protobuf.ByteString does not implement Add(System.Object).
   at System.Xml.Serialization.TypeScope.GetEnumeratorElementType(Type type, TypeFlags& flags)
   at System.Xml.Serialization.TypeScope.ImportTypeDesc(Type type, MemberInfo memberInfo, Boolean directReference)
   at System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo source, Boolean directReference, Boolean throwOnError)
   at System.Xml.Serialization.StructModel.GetPropertyModel(PropertyInfo propertyInfo)
   at System.Xml.Serialization.StructModel.GetFieldModel(MemberInfo memberInfo)
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel model, XmlRootAttribute root, String defaultNamespace, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
   at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
   at System.Xml.Serialization.XmlSerializer..ctor(Type type)
   at P.Main() in C:\Users\marc\source\repos\ConsoleApp14\Program.cs:line 16

--
Regards,

Marc
Generated.cs

Arun

unread,
Aug 22, 2019, 2:52:18 AM8/22/19
to Marc Gravell, Protocol Buffers
Thanks Marc for your analysis..   Thanks for clarifying  me that generated code for my proto files does not suitable for all serialized API, I was thinking the other way. 

And, generated code through protobuf-net's tooling works very well . But, I am not able to generated code for all my proto's even though , "  my dependant proto files are in same folder " 

" Paths should be relative to the import paths, not rooted
Parameter name: name
   at Google.Protobuf.Reflection.FileDescriptorSet.Add(String name, Boolean includeInOutput, TextReader source, FileDescriptorProto fromFile)
   at protogen.Program.Main(String[] args) ".  

And , thanks for the solution by giving this clue , --> generated code *tested* against the specific API it is designed for - Google's protobuf library  .. 

I am converting my input to object via Google's protobuf library JSON parser and it works fine. It sounds may insane, This is what I doing now, xml --> json --> CS object --> serialize and writing to stream

Thanks for your help !!! Much appreciated ! 
--
-- Arun

Reply all
Reply to author
Forward
0 new messages