I am trying to deserialize incoming XML data with C#/.NET 3.5 using a class hierarchy that starts with a class tagged as XmlRoot and contains classes and members with the required attributes. This works so far.
Now I have come across input data that looks approximately like this:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:foo xmlns:ns1="http://some.name/space">
<ns1:bar>
<payload interesting="true">
<stuff type="interesting"/>
</payload>
</ns1:bar>
</ns1:foo>
The outermost two elements - ns1:foo and ns1:bar - are results of the serialization process in the remote system that I have no control over. They always exist in exactly the same constellation and are of no interest whatsoever.
Following my current approach, I could write a class Foo that references a class Bar which in turn references the class Payload. However, since I'm not interested in the foo and bar elements, is there a way to skip them during the deserialization and have the Deserializer return only the payload? If not: Is it possible to make a class FooPayload with an XmlRoot(ElementName = "foo", Namespace = "http://some.name/space")attribute, but then somehow skip the bar and payload levels so that the FooPayload class directly contains properties named Interesting and Stuff?
I modified your XML to correct for errors. See interesting solution below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<ns1:foo xmlns:ns1=\"http://some.name/space\">" +
"<ns1:bar>" +
"<ns1:payload interesting=\"true\">" +
"<ns1:stuff type=\"interesting\"/>" +
"</ns1:payload>" +
"</ns1:bar>" +
"</ns1:foo>";
//remove prefix
string pattern = "(</?)([^:]*:)";
Regex expr = new Regex(pattern);
input = expr.Replace(input, "$1");
XmlSerializer xs = new XmlSerializer(typeof(Foo));
StringReader s_reader = new StringReader(input);
XmlTextReader reader = new XmlTextReader(s_reader);
Foo foo = (Foo)xs.Deserialize(reader);
foo.GetStuff();
}
}
[XmlRoot(ElementName = "foo")]
public class Foo
{
[XmlElement("bar")]
public Bar bar { get; set; }
private Boolean interesting;
private string type;
public void GetStuff()
{
interesting = bar.payload.interesting;
type = bar.payload.stuff.type;
}
}
[XmlRoot("bar")]
public class Bar
{
[XmlElement("payload")]
public Payload payload { get; set; }
}
[XmlRoot("payload")]
public class Payload
{
[XmlAttribute("interesting")]
public Boolean interesting { get; set; }
[XmlElement("stuff")]
public Stuff stuff { get; set; }
}
[XmlRoot("stuff")]
public class Stuff
{
[XmlAttribute("type")]
public string type { get; set; }
}
}
Related
I've searched for hours and found similar results but not for this specific scenario. Consider the following XML file.
<root>
<largeImages>
<largeImage>
<url>./imageLarge.jpg</url>
<height>480</height>
<width>640</width>
</largeImage>
</largeImages>
<smallImages>
<smallImage>
<url>./imageSmall.jpg</url>
<height>240</height>
<width>320</width>
</smallImage>
</smallImages>
</root>
What I'm trying to do is deserialize this into a single array of images instead of 2 arrays.
public class root {
[XmlArray("largeImages")]
[XmlArrayItem("largeImage")]
public image[] largeImages { get; set; }
[XmlArray("smallImages")]
[XmlArrayItem("smallImage")]
public image[] smallImages { get; set; }
}
This class gets me 2 arrays. root.largeImages and root.smallImages. Since my application doesn't care about large or small images I'd like to deserialize into the single array root.images. I've tried variations of XmlArray, XmlArrayItem, XmlElement and even XmlChoiceIdentifier without any success. I'm thinking something along the lines of the following which won't compile because apparently the XmlArrayAttribute can only be used once per property.
[XmlArray("largeImages")]
[XmlArray("smallImages")]
[XmlArrayItem("largeImage")]
[XmlArrayItem("smallImage")]
public image[] images { get; set; }
Obviously I could merge the 2 arrays in code after the XML is deserialized but it seems like this should be a simple thing to do.
XPATH is probably your answer assuming you don't really care about having it mapped to a class. XPath wildcards on node name gives an example of how you'd select multiple items - http://csharp.net-tutorials.com/xml/using-xpath-with-the-xmldocument-class/ gives an example of how it's used in C#.
Another way muight be using XSLT: Using the code: How to apply an XSLT Stylesheet in C# and XSLT like Combining elements from 2 lists in XSLT should get you what you want.
Personally I'd go with whatever makes life easiest since it doesn't look like you really care in this example what the intermediate data structure is.
Given that the answer from How to define multiple names for XmlElement field? works primarily for elements not arrays, the easiest solution would seem to be to introduce a surrogate property for one of the image elements, say <smallImages>:
public class root
{
[XmlArray("largeImages")]
[XmlArrayItem("largeImage")]
public List<image> Images { get; set; } = new List<image>();
[XmlArray("smallImages")]
[XmlArrayItem("smallImage")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List<image> SmallImagesSurrogate { get { return Images; } }
public bool ShouldSerializeSmallImagesSurrogate() { return false; }
}
Notes:
When deserializing to a pre-allocated List<T>, XmlSerializer will simply append deserialized elements to the existing list. That allows elements from both <largeImages> and <smallImages> to be concatenated into the same collection without data loss.
The surrogate property must be public but can be made "less visible" by setting attributes such as [Browsable(false)].
XmlSerializer can successfully deserialize get-only collection properties as long as the collection is pre-allocated. I took advantage of this to avoid adding a setter for the surrogate.
In case you need to re-serialize your root, the method ShouldSerializeSmallImagesSurrogate() will prevent the images array from being double-serialized. (For an explanation of why, see ShouldSerialize*() vs *Specified Conditional Serialization Pattern.) Instead all the images will serialize under <largeImages>. This method does not affect deserialization.
Sample working .Net fiddle.
Simple!!! I do it all the time. The trick is with root that you need to use Elements() and then use FirstOrDefault() to get only one.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Root root = doc.Elements("root").Select(x => new Root() {
Images = x.Descendants("largeImage").Select(z => new Image() {
url = (string)z.Element("url"),
height = (int)z.Element("height"),
width = (int)z.Element("width")
}).ToList()
}).FirstOrDefault();
root.Images.AddRange(doc.Descendants("smallImage").Select(z => new Image() {
url = (string)z.Element("url"),
height = (int)z.Element("height"),
width = (int)z.Element("width")
}).ToList());
}
}
public class Root
{
public List<Image> Images { get; set; }
}
public class Image
{
public string url { get; set; }
public int height { get; set; }
public int width { get; set; }
}
}
I had a challenge with model serialization, and thanks to MSDN I found the answer. Here is my solution:
public class Document
{
[XmlElement(ElementName = "seller_id")]
public string SellerId { get; set; }
[XmlArray(ElementName = "order_details")]
[XmlArrayItem(Type = typeof(SgtinCode), ElementName = "sgtin")]
[XmlArrayItem(Type = typeof(SsccCode), ElementName = "sscc")]
public Code[] Codes { get; set; }
}
public abstract class Code
{
[XmlText]
public string Value { get; set; }
}
public class SgtinCode : Code
{ }
public class SsccCode : Code
{ }
Model setup:
var document = new Document
{
SellerId = Guid.NewGuid().ToString(),
Codes = new Code[]
{
new SsccCode { Value = "111700126101510000000000011" },
new SsccCode { Value ="111700126101510000000000012" },
new SsccCode { Value ="111700126101510000000000013" },
new SgtinCode { Value = "abc" }
}
}
And XML output:
<?xml version="1.0" encoding="utf-8"?>
<documents>
<foreign_shipment>
<seller_id>fb2d35e7-c5d1-43ad-a272-89f897f41058</seller_id>
<order_details>
<sscc>111700126101510000000000011</sscc>
<sscc>111700126101510000000000012</sscc>
<sscc>111700126101510000000000013</sscc>
<sgtin>abc</sgtin>
</order_details>
</foreign_shipment>
</documents>
I am converting my working XML serialization so that the model classes inherit from abstract base classes (to allow for future use of different serial formats).
My serialization as-is is working fine but when I switch to using models derived from a base class I get all kinds of exceptions so I'm unsure of how to proceed.
My class base class is:
namespace Data
{
public abstract class Configuration
{
public abstract string Schema { get; set; }
public abstract Command[] Commands { get; set; }
}
public abstract class Command
{
public abstract string Name { get; set; }
}
}
My derived concrete class (the class that is working just fine before it was derived) is then in a child namespace:
namespace Data.Xml
{
[Serializable()]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public class Configuration : Data.Configuration
{
[XmlAttribute("noNamespaceSchemaLocation",
Namespace = System.Xml.Schema.XmlSchema.InstanceNamespace)]
public override string Schema { get; set; }
[XmlArrayItem("Command", IsNullable = false)]
public override Data.Command[] Commands { get; set; }
}
[Serializable()]
public class Command : Data.Command
{
public override string Name { get; set; }
}
}
I call the serializer within that child namespace like so:
public override Data.Configuration DeserializeConfig(StreamReader config)
{
var cs = new XmlSerializer(typeof(Configuration),
new Type[] { typeof(Command) });
return (Configuration)ConfigSerializer.Deserialize(ConfigStreamReader);
}
public override string SerializeConfig(Data.Configuration c, Encoding encoding)
{
string Output = null;
var Stream = new MemoryStream();
using (var Writer = new XmlTextWriter(Stream, encoding))
{
Writer.Formatting = Formatting.Indented;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("xsi", XmlSchema.InstanceNamespace);
(new XmlSerializer(typeof(Configuration))).Serialize(Writer, c, ns);
Output = encoding.GetString(Stream.ToArray());
}
Stream.Dispose();
return Output;
}
The resulting XML should look like:
<?xml version="1.0" encoding="utf-8"?>
<Configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="SomeSchema.xsd">
<Commands>
<Command>
<Name>SomeNameValue</Name>
</Command>
</Commands>
</Configuration>
I am seeing the following exception when attempting to instantiate the serializer (first line in DeserializeConfig() method above):
InvalidOperationException: Types 'Data.Command' and 'Data.Xml.Command' both use the XML type name, 'Command', from namespace ''. Use XML attributes to specify a unique XML name and/or namespace for the type.
I'm not really sure why the serializer is trying to create something from the base class, sure the names are the same, that's kind of the idea of derivation and namespaces etc ... How do I properly mark this up with attributes to have it de/serialize properly?
FYI: I did see several questions already on this, but the answers all seemed specific enough to the askers requirements that I could not work out how to apply the information to this, seemingly simple, scenario.
Update: I figured out how to pass included types into the serializer at instantiation instead of having to annotate the base class so I have removed that part from my question and updated the code. This outdates bruno's suggestion and my response (although the suggested question still does not apply).
Update: I attempted to separate the names in XML namespaces by adding the derived class to a namespace (i.e. adding [XmlElement(Namespace = "http://www.foo.com/data/xml")] to each property in the derived class) but this made no difference as the serializer still seems to "see" both the base and derived class together and so thinks they're both in that namespace.
Finally flipping figured most of this out.
I stepped back and started with a very simple working non-derived example and worked up to what I needed.
There were two things going on here. First the clashing type names, then the clashing property names. While I had bits of each of these right, the amount of permutations of options for structuring each when combined together had me confused.
To prevent the abstract and derived type names from clashing when serialized I needed to make the derived class type anonymous (here using the XmlType attribute).
To stop the property names clashing I needed to ignore both the property in the derived class and the base class. To do this without editing the base class I was missing a vital piece, XmlAttributeOverrides. I had seen this mentioned in the MSDN documentation for XmlSerializer.Serialize() but the information there was pretty minimal in explaining what it pertained to. This answer to another question led me to David Woodward's excellent explanation.
I have yet to try any of this with a derived type list property, or with deserialization.
Below is complete basic example of a program that outputs a string with some serialized XML on the console output:
using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var TestBar = new MyXml.Bar()
{
Name = "John Smith",
};
Serializer s = new MyXml.Serializer();
var TestOutput = s.Serialize(TestBar);
Console.WriteLine(TestOutput);
}
}
public abstract class Bar
{
public abstract string Name { get; set; }
}
public abstract class Serializer
{
public abstract string Serialize(Bar bar);
}
namespace MyXml
{
public class Serializer : Test.Serializer
{
public override string Serialize(Test.Bar bar)
{
string Output = null;
var Stream = new MemoryStream();
var Encoding = new UTF8Encoding(false, true);
// Ignore the Name property in the *base* class!
var ao = new XmlAttributeOverrides();
var a = new XmlAttributes();
a.XmlElements.Clear(); // Clear any element attributes
a.XmlAttribute = null; // Remove any attribute attributes
a.XmlIgnore = true; // Set the ignore attribute value true
ao.Add(typeof(Test.Bar), "Name", a); // Set to use with Test.Bar.Name
using (var Writer = new XmlTextWriter(Stream, Encoding))
{
Writer.Formatting = Formatting.Indented;
var s = new XmlSerializer(typeof(Bar), ao);
s.Serialize(Writer, bar);
Output = Encoding.GetString(Stream.ToArray());
}
Stream.Dispose();
return Output;
}
}
[Serializable]
[XmlType(AnonymousType = true)] // Make type anonymous!
[XmlRoot(IsNullable = false)]
public class Bar : Test.Bar
{
[XmlIgnore] // Ignore the Name property in the *derived* class!
public override string Name
{
get => Unreverse(ReverseName);
set => ReverseName = Reverse(value);
}
[XmlElement("Name", IsNullable = false)]
public string ReverseName { get; set; }
private string Unreverse(string name)
{
return "John Smith"; // Smith, John -> John Smith
}
private string Reverse(string name)
{
return "Smith, John"; // John Smith -> Smith, John
}
}
}
}
I'm trying to deserialize a XML document, with different root namespaces, to a C# class.
In short, I want to deserialize multiple versions of a similar xml document like so:
<IndexRoot Code="0664" xmlns="http://tempuri/2012/1.0">
<Name>Foo</Name>
<Color>blue</Color>
...
</IndexRoot>
<IndexRoot Code="0678" xmlns="http://tempuri/2012/2.0">
<Name>Bar</Name>
<Character>Smurf</Character>
</IndexRoot>
Each version can obviously have different elements below it, and whilst most elements are the same there are some differences. In the sample above the Name attribute is available in each version, while the Color/Character are unique to each version.
Ideally, I want to abstract this to a simple function that gives me a reflected concrete class. Like so:
public IndexRoot Get(string fileName) {
var doc = XDocument.Load(fileName);
return xmlSerializer.Deserialize<IndexRoot>(doc);
}
In my current setup this fails, because it is required to provide a single namespace on the root element for the deserializer to work:
[Serializable, XmlRoot(ElementName = "IndexRoot", Namespace = "http://tempuri/2012/2.0")]
public class IndexRoot
{
[XmlAttribute("Code")]
public string Code { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
}
As you can see, the hardcoded namespace will work for 2.0 versions but will fail for other versions with the exception: "IndexRoot xmlns='http://tempuri/2012/1.0' was not expected."
The question: how can I deserialize the XML to a C# object, taking the multiple root namespaces into consideration?
Ideally, this would be reflected to a concrete type per version. But I'll even settle for getting a "base class" with the common, shared properties. Either way, I'm currently stuck with the current hardcoded Namespace value on the [XmlRoot].
I have tried:
Adding duplicate [XmlRoot] attributes (which is not supported)
Create a base class (BaseIndexRoot), deriving two instances from it and decorating these derivates with the [XmlRoot] attribute (same "was not expected" error)
Removing the Namespace all together also results in the "was not expected" error
I usually do it like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication91
{
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<IndexRoot Code=\"0664\" xmlns=\"http://tempuri/2012/1.0\">" +
"<Name>Foo</Name>" +
"<Color>blue</Color>" +
"</IndexRoot>";
XDocument doc = XDocument.Parse(xml);
XElement indexRoot = (XElement)doc.FirstNode;
XNamespace ns = indexRoot.Name.Namespace;
string name = indexRoot.Element(ns + "Name").Value;
XElement indexRoot2 = doc.Descendants().Where(x => x.Name.LocalName == "IndexRoot").FirstOrDefault();
}
}
}
I was able to solve the problem of deserializing XML documents with the same structure, but with different namespaces, with the construction below.
First, I created derived classes for each specific version and decorated those with the namespace:
[Serializable, XmlRoot(ElementName = "IndexRoot", Namespace = "http://tempuri/2012/1.0")]
public class IndexRootV10 : IndexRoot { }
[Serializable, XmlRoot(ElementName = "IndexRoot", Namespace = "http://tempuri/2012/2.0")]
public class IndexRootV20 : IndexRoot { }
public class IndexRoot
{
[XmlAttribute("Code")]
public string Code { get; set; }
[XmlElement(ElementName = "Code")]
public string Code { get; set; }
}
All I needed to do after this was to simply modify the deserialize function to determine which version (dervied class) to apply:
public IndexRoot Get(string fileName) {
var doc = XDocument.Load(fileName);
switch (doc.Root?.Name.NamespaceName)
{
case "http://tempuri/2012/1.0":
response = xmlSerializer.Deserialize<IndexRootV10>(doc);
break;
case "http://tempuri/2012/2.0":
response = xmlSerializer.Deserialize<IndexRootV20>(doc);
break;
default:
throw new NotSupportedException($"Unsupported xmlns namespace (version): {doc.Root.Name.NamespaceName}");
}
}
Although tis is the part I'm least happy about, due to the hardcoded switch statement, it does work properly. Somehow I can't help but think there are more sofisticated ways to solve this.
On the plus side, if a specific version has different properties, the derived classes are now ideally suited to reflect this.
I have been able to use ShouldSerializeProperty pattern with XmlSerializer to ignore a property of type ICollection<>. Below is the sample code. It works if I use XmlIgnore on ListOfTestClassB property but I want to achieve the same functionality utilizing ShouldSerialize pattern. If I run the code below I get the 'System.InvalidOperationException' saying:
Cannot serialize member ConsoleApplication3.TestClassA.ListOfTestClassB of type System.Collections.Generic.ICollection`1[[ConsoleApplication3.TestClassB, ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
Can anyone highlight what am I missing?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
[Serializable]
public class TestClassA
{
public int A { get; set; }
public int B { get; set; }
public string C { get; set; }
public TestClassB TestClass { get; set; }
public virtual ICollection<TestClassB> ListOfTestClassB { get; set; }
public bool ShouldSerializeListOfTestClassB()
{
return false;
}
}
[Serializable]
public class TestClassB
{
public int Int32 { get; set; }
public string String { get; set; }
}
class Program
{
static object GetObject()
{
return new TestClassA { A = 1, B = 2, C = "test class a", TestClass = new TestClassB { Int32 = 11, String = "test class b"} };
}
static void Main(string[] args)
{
var result = new StringBuilder();
var entity = GetObject();
var ser = new XmlSerializer(entity.GetType());
var settings = new XmlWriterSettings { OmitXmlDeclaration = true };
using (var stream = new MemoryStream())
{
// make a copy of the entity - we do not want to serialize ZIP file !
var formatter = new BinaryFormatter();
formatter.Serialize(stream, entity);
stream.Position = 0;
entity = formatter.Deserialize(stream);
}
// serialize object
ser.Serialize(XmlWriter.Create(result, settings), entity);
Console.WriteLine(result.ToString());
Console.ReadLine();
}
}
}
This is where the check happens: http://referencesource.microsoft.com/#System.Xml/System/Xml/Serialization/Models.cs,249.
As you can see, it doesn't call the ShouldSerialize* method until after it's already identified fields/properties to serialize. So, your ListOfTestClassB has to be serializable, or it must be decorated with [XmlIgnore]. In order to be serializable your property must be of a concrete type that has the [Serializable] attribute applied.
There's a workaround if you can't modify the class. One of the overloads of the XmlSerializer.Serialize(...) method accepts an overrides object. I've created a simple example below:
[Serializable]
public class Foo
{
public IList<int> Numbers { get; set; }
public string TheNumber { get; set; }
}
class Program
{
private static void Main(string[] args)
{
var attributes = new XmlAttributes
{
XmlIgnore = true
};
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Foo), "Numbers", attributes);
var serializer = new XmlSerializer(typeof(Foo), overrides);
// the rest of this is for demo purposes.
// the code above is whats important
//
using (var ms = new MemoryStream())
using (var reader = new StreamReader(ms))
{
serializer.Serialize(ms, new Foo() { TheNumber = "5" });
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
Debug.WriteLine(reader.ReadToEnd());
}
}
}
This generates:
<?xml version="1.0"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TheNumber>5</TheNumber>
</Foo>
As you can see, I'm dynamically adding the XmlIgnore attribute to my Numbers element, and the serializer consequently ignores it. :) You should have no trouble adapting this to your own code.
Note: As noted by dbc, it's important to cache this serializer and re-use it, otherwise you're going to have a lot of memory leaks. You can keep a static reference to it, or use a hashtable to store different serializers for different types.
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
If you use XmlIgnore, then it will not care about that property at all. If you use ShouldSerialize, it doesn't know until runtime whether it should serialize that type, so it must be able to. In this case, the type you're trying to serialize must be a concrete class. Try using List<TestClassB>.
protobuf-net uses nested protobuf constructs to support inheritance. However, can it be made to push properties into a flat target class that has the same properties as the inherited "serialized" version?
See the test example below. Needless to say the result of the Flat namespace is null for both properties.
Possible solution: copy data into flat.B first on a property by property basis.
Note: this is not the prefered option.
using System;
namespace hierarchy
{
using ProtoBuf;
[ProtoContract]
public class A
{
[ProtoMember(1)]
public string prop1 { get; set; }
}
[ProtoContract]
public class B : A
{
public B()
{
}
[ProtoMember(1)]
public string prop2 { get; set; }
public override string ToString()
{
return "prop1=" + prop1 + ", prop2=" + prop2;
}
}
}
namespace flat
{
using ProtoBuf;
[ProtoContract]
public class B
{
[ProtoMember(1)]
public string prop1 { get; set; }
[ProtoMember(2)]
public string prop2 { get; set; }
public override string ToString()
{
return "prop1=" + prop1 + ", prop2=" + prop2;
}
}
}
namespace TestProtoSerialization
{
using ProtoBuf;
using System.IO;
public class Test2
{
public void Test()
{
var hb = new hierarchy.B();
hb.prop1 = "prop1";
hb.prop2 = "prop2";
var ms = new MemoryStream();
Serializer.Serialize<hierarchy.B>(ms, hb);
var flatB = Serializer.Deserialize<flat.B>(ms);
Console.WriteLine(hb.ToString()); // <----- Output: prop1=prop1, prop2=prop2
Console.WriteLine(flatB.ToString()); // <----- Output: prop1=, prop2=
}
}
public class Program
{
private static void Main(string[] args)
{
var o2 = new Test2();
o2.Test();
}
}
}
Not directly, and I'm not sure there is a great need to. Maybe I am missing something in the example...
To pick up on the key point - even forgetting about inheritance you've broken the contract - te fields in your exampl are 1 & 1 in one model and 1 & 2 in the other.
It really depends what your objective is; if you just want to push the data over, then sure you can set up a RuntimeTypeModel that only knows about the derived type (disable automatic configuration and add the fields manually). This will then only work for the derived type (obviously), but will output the data as expected by the flat model:
var model = TypeModel.Create();
model.Add(typeof(B), false)
.Add("prop1", "prop2");
Then use model.Serialize etc.
However, writing a flat conversion method on c#, or using AutoMapper would be more obvious. I would only use the above if my objective is to remove the inheritance from the output, for example for interoperability reasons.