I have 2 derived classes that will be serialized into xml.
While the code works fine (XmlSerializer, nothing strange), the serialization of DataScenario causes its MyData property items to produce Xmlelement names from the base class name:
<DataScenario>
<MyData>
<ScenarioData/>
<ScenarioData/>
<ScenarioData/>
</MyData>
<DataScenario>
Instead, i'm trying to have these items produce XmlElement names from their derived classes
<DataScenario>
<MyData>
<type1/>
<type1/>
<type2/>
</MyData>
<DataScenario>
Is this even possible? Keep in mind I need to deserialize as well; I'm unsure whether the Deserialize process will understand that derived objects need to be created.
Sample code i'm using is as follows.
[Serializable]
[XmlInclude(typeof(Type1))]
[XmlInclude(typeof(Type2))]
public class Scenario
{
[XmlElement("location")]
public string Location { get; set; }
[XmlElement("value")]
public string Value { get; set; }
public Scenario()
{
}
}
[Serializable]
[XmlType("type1")]
public class Type1 : Scenario
{
public FillPointData() : base() { }
}
[Serializable]
[XmlType("type2")]
public class Type2 : Scenario
{
public TestData() : base() { }
}
//Hosting class of all scenarios
public DataScenario()
{
public List<Scenario> MyData{ get; set; }
}
You can define what kind of Elements are in the Collection with the XmlArrayItem attribute.
If the Type is known (defined as you did with the XmlInclude attribute) it will create Tags "Type1", "Type2". If the Types are not known, it will still create a Tag called ScenarioData with an Attribute xsi:type="Type1" which is used to map the type while deserialization.
[XmlArrayItem(typeof(Type1))]
[XmlArrayItem(typeof(Type2))]
Public List<Scenario> Children
{
// getter & setter
}
Related
I have been using protobuf.net for a while and it is excellent. I can have a class which is inherited from a base class, I can serialise the derived class by using ProtoInclude statements in the base class. If my base class originally had only say two ProtoInclude statements when the object was serialised, say
[ProtoInclude(100, typeof(Vol_SurfaceObject))]
[ProtoInclude(200, typeof(CurveObject))]
internal abstract class MarketDataObject
I can still deserialise that same object in to code that has evolved to have more derivations:
[ProtoInclude(100, typeof(Vol_SurfaceObject))]
[ProtoInclude(200, typeof(CurveObject))]
[ProtoInclude(300, typeof(StaticDataObject))]
internal abstract class MarketDataObject
So far so good (in fact excellent, thanks Marc). However, now what if I want to have a base class even lower then my current base class here (in this case, MarketDataObject). Such that I would have
[ProtoInclude(100, typeof(Vol_SurfaceObject))]
[ProtoInclude(200, typeof(CurveObject))]
[ProtoInclude(300, typeof(StaticDataObject))]
internal abstract class MarketDataObject : LowerStillBaseClass
{ blah }
[ProtoInclude(10, typeof(MarketDataObject))]
internal abstract class LowerStillBaseClass
{ blah }
Whilst the code will of course work, will I be still be able to deserialise the initial objects that were serialised when the object had only 2 ProtoInclude statements to this new form of the MarketDataObject class?
This will not work purely with static protbuf-net attributes. Simplifying somewhat, imagine you start with the following :
namespace V1
{
[ProtoContract]
internal class MarketDataObject
{
[ProtoMember(1)]
public string Id { get; set; }
}
}
And refactor it to be the following:
namespace V2
{
[ProtoInclude(10, typeof(MarketDataObject))]
[ProtoContract]
internal abstract class LowerStillBaseClass
{
[ProtoMember(1)]
public string LowerStillBaseClassProperty { get; set; }
}
[ProtoContract]
internal class MarketDataObject : LowerStillBaseClass
{
[ProtoMember(1)]
public string Id { get; set; }
}
}
Next, try to deserialize a created from the V1 class into a V2 class. You will fail with the following exception:
ProtoBuf.ProtoException: No parameterless constructor found for LowerStillBaseClass
The reason this does not work is that type hierarchies are serialized base-first rather than derived-first. To see this, dump the protobuf-net contracts for each type by calling Console.WriteLine(RuntimeTypeModel.Default.GetSchema(type)); For V1.MarketDataObject we get:
message MarketDataObject {
optional string Id = 1;
}
And for V2.MarketDataObject:
message LowerStillBaseClass {
optional string LowerStillBaseClassProperty = 1;
// the following represent sub-types; at most 1 should have a value
optional MarketDataObject MarketDataObject = 10;
}
message MarketDataObject {
optional string Id = 1;
}
MarketDataObject is getting encoded into a message with its base type fields first, at the top level, then derived type fields are recursively encapsulated inside a nested optional message with a field id that represents its subtype. So when a V1 message is deserialized to a V2 object, no subtype field is encountered, the correct derived type is not inferred, and derived type values are lost.
One workaround is to avoid using [ProtoInclude(10, typeof(MarketDataObject))] and instead populate the base class members in the derived type's contract programmatically using the RuntimeTypeModel API:
namespace V3
{
[ProtoContract]
internal abstract class LowerStillBaseClass
{
[ProtoMember(1)]
public string LowerStillBaseClassProperty { get; set; }
}
[ProtoContract]
internal class MarketDataObject : LowerStillBaseClass
{
static MarketDataObject()
{
AddBaseTypeProtoMembers(RuntimeTypeModel.Default);
}
const int BaseTypeIncrement = 11000;
public static void AddBaseTypeProtoMembers(RuntimeTypeModel runtimeTypeModel)
{
var myType = runtimeTypeModel[typeof(MarketDataObject)];
var baseType = runtimeTypeModel[typeof(MarketDataObject).BaseType];
if (!baseType.GetSubtypes().Any(s => s.DerivedType == myType))
{
foreach (var field in baseType.GetFields())
{
myType.Add(field.FieldNumber + BaseTypeIncrement, field.Name);
}
}
}
[ProtoMember(1)]
public string Id { get; set; }
}
}
(Here I am populating the contract inside the static constructor for MarketDataObject. You might want to do it elsewhere.) The schema for V3. looks like:
message MarketDataObject {
optional string Id = 1;
optional string LowerStillBaseClassProperty = 11001;
}
This schema is compatible with the V1 schema, and so A V1 message can be deserialized into a V3 class without data loss. Sample fiddle.
Of course, if you are moving a member from MarketDataObject to LowerStillBaseClass you will need to ensure that the field id stays the same.
The disadvantage of this workaround is that you lose the ability to deserialize an object of type LowerStillBaseClass and have protobuf-net automatically infer the correct derived type.
I have a scenario close to the one in this thread . I wanted to provide a more specific example but wasn't sure how to ask it on that thread and make it look pretty. So I started this one. I have a collection of BaseObjects within a class. I would like to Deserialize this class with the Xml output of the mixed collection of derived classes.
public class RootElementClass
{
public BaseTypeCollection Collection{ get; set; }
}
public class BaseType { public int Id{get;set;} }
public class BaseTypeCollection : System.ComponentModel.BindingList<BaseType>{}
public class Derived1BaseType : BaseType { public string Derived1Name{get;set;} }
public class Derived2BaseType : BaseType { public string Derived2Name{get;set;}
public string DerivedOtherProperty{get;set;} }
How do I get an output of something like this:
<RootElementClass>
<Collection>
<BaseObject>
<Id>...</Id>
<Derived1Name>...</Derived1Name>
</BaseObject>
<BaseObject>
<Id>...</Id>
<Derived2Name>...</Derived2Name>
<DerivedOtherProperty>...</DerivedOtherProperty>
</BaseObject>
</Collection>
</RootElementClass>
I am modifying a set of classes that are being serialized, and I have a question that I cannot find the answer to. The old classes had a very large class called Control that is further classified by a ControlType attribute
enum ControlType
{
ControlType1 = 0,
ControlType2 = 1
}
public class Control
{
[XmlAttribute("a")]
public string a { get; set; }
[XmlAttribute("b")]
public string b { get; set; }
[XmlAttribute("Type")]
public ControlType Type {get; set;)
}
in the simplified sample above the original designer did not separate out the class into subclasses. what we really want is
class baseControl
{
[XmlAttribute("Type")]
public ControlType Type {get; set;}
}
class Control1 : baseControl
{
[XmlAttribute("a")]
public string a { get; set; }
}
class Control2 : baseControl
{
[XmlAttribute("b")]
public string b { get; set; }
}
we want to separate out the classes but we want the original xml to be compatible
in the old hierarchy all controls types (defined by ControlType) were serialized as
<Control Type="ControlType1" a="xxxx" />
<Control Type="ControlType2" b="xxxxx" />
if we use the new structures obviously the new structure would look like
<Control1 Type="ControlType1" a="xxxx" />
<Control2 Type="ControlType2" b="xxxxx" />
but we really want to serialize all of the new derived classes as "Control" and when we deserialize we want to change the allocated type to the derived type based on the value of an Attribute.
Is this possible?
The only chance to accomplish such behavior is to implemnent IXmlSerializable on the class corresponding to the surrounding element, and provide a custom (de)serialization behavior.
public class ControlContainer : IXmlSerializable
{
// a single / array of / list of BaseControls
public BaseControl Control { get; set; }
// … any other properties
// … implement IXmlSerializable here to have Control
// and any other properties (de)serialized
}
Take the following example code:
public abstract class ElementBase
{
}
public class ElementOne : ElementBase
{
}
public class ElementTwo : ElementBase
{
[XmlElement("element-one", typeof(ElementOne))]
[XmlElement("element-two", typeof(ElementTwo))]
public ElementBase[] SubElements { get; set; }
}
[XmlRoot("root-element")]
public class RootElement
{
[XmlElement("element-one", typeof(ElementOne))]
[XmlElement("element-two", typeof(ElementTwo))]
public ElementBase[] SubElements { get; set;}
}
The attributes on ElementOne.SubElements and ElementTwo.SubElements need to stay in sync (i.e., attributes added to one will need to be added to the other, and arguments need to stay the same), The reason for this is that in the xml, <element-one> and elements can both appear as subelements to <root-element> and <element-two>. The elements can be in any order, and the order is important. Also, there will probably be more subelements in the future. The way it is currently coded will make maintenance tedious and error-prone because of the need to maintain two separate places for attributes.
Is there a way to have these attributes "shared" between the two properties, such that a single edit will affect them both? I tried the following:
public class CommomAttribute : Attribute
{
public XmlElementAttribute f = new XmlElementAttribute("element-one", typeof(ElementOne));
public XmlElementAttribute l = new XmlElementAttribute("element-two", typeof(ElementTwo));
}
I then replaced the redundant attributes on the above classes' properties with a single [Command]. This didn't work.
An alternative question: is there a more elegant way to solve this problem?
You can try this if you don't mind having to go one level deeper to get to you sub-element items:
public abstract class ElementBase
{
}
public class ElementOne : ElementBase
{
}
public class ElementTwo : ElementBase
{
public SubElementList SubElements { get; set; }
}
public class SubElementList
{
[XmlElement("element-one", typeof(ElementOne))]
[XmlElement("element-two", typeof(ElementTwo))]
public ElementBase[] Items { get; set; }
}
[XmlRoot("root-element")]
public class RootElement
{
public SubElementList SubElements { get; set; }
}
Off the top of my head, I'd do the following:
On the ctor of each class (One and Two), require and instance of ElementBase and keep it as a private attribute (let's say, "SyncingElement")
Modify the setter of SubElements, to sync with the instance of "SyncingElement"
This way, SubElements on both objects would have the same memory address (same instance). So, if someone get the instance of SubElements from One modifies the object at index [2] (for example), it would affect SubElements at Two as well.
I am trying to serialise some C# classes to XML. Things were going fine until I tried to introduce some inherited classes.
The classes are [edited for size]
public class ParticipationClass
{
[XmlAttribute]
public string typeCode;
[XmlAttribute]
public string contextControlCode;
public ParticipationClass()
{
}
}
public class p_recordTarget : ParticipationClass
{
public p_recordTarget()
{
typeCode = "RCT";
contextControlCode = "OP";
}
}
when using the classes the following code is used :
public class Test
{
// other class attributes excluded..
[XmlElement]
public ParticipationClass recordTarget;
private void Test()
{
recordTarget = new p_recordTarget();
}
}
The serialisation fails with an InvalidOperationException, looking in the exception detail I can see "Message="The type itk.cda.cdaclasses.p_recordTarget was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."
So I guess I need an XmlInclude, but I am not sure how.
In a nutshell, you need to use the attribute on the base class to let the serializer know about the derived classes:
[XmlInclude(typeof(p_recordTarget))]
public class ParticipationClass {
// ...
}
This may help you out:
http://www.codeproject.com/KB/XML/xmlserializerforunknown.aspx
Like this:
[XmlInclude(typeof(p_recordTarget))]
public class ParticipationClass
{
[XmlAttribute]
public string typeCode;
[XmlAttribute]
public string contextControlCode;
public ParticipationClass()
{
}
}