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()
{
}
}
Related
I have a class Response with generic parameter:
public class Response<T> where T : class {
public bool Result;
public T Data;
}
Also, I have a class Instance with simple parameters
public sealed class Instance {
public long Rank { get; set; }
public int ID_Member { get; set; }
}
And then I have a class where I use last ones
public sealed class InstanceResponse : Response<IList<Instance>> { }
And I try to add a constructor to last class and don't understand how to do it
I've tried like there, but it's doesn't work, JsonString contains serialized class InstanceResponse
public sealed class InstanceResponse : Response<IList<Instance>> {
public InstanceResponse(string JsonString) {
this = JsonConvert.DeserializeObject<InstanceResponse>(JsonString);
}
}
I've got an error Cannot assign to 'this' because it is read-only
How it possible?
It's not possible to deserialize json to the object and assign it directly in ctor to the object itself using this keyword.
Provided that
Json contains serialized class InstanceResponse
You can do something like this:
public sealed class InstanceResponse : Response<IList<Instance>> {
public InstanceResponse(string JsonString) {
var response = JsonConvert.DeserializeObject<InstanceResponse>(JsonString);
this.Data = response.Data;
this.Result = response.Result;
}
}
Another possible solution is to deserialize json in a code that creates instance of InstanceResponse (call's ctor) somewhere.
Instead of:
var response = new InstanceResponse(json);
You could deserialize json right there:
var response = JsonConvert.DeserializeObject<InstanceResponse>(json);
P.S.
With that being said, an interesting point was raised by #Lasse Vågsæther Karlsen regarding the subject. It is actually possible to assign something to this however it is only working inside of a structs ctor and use cases for it are very limited...(thanks Lasse)
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 have a class like this
public class BaseClass
{
public string Request { get; set;}
}
and I have the class like this :
public class ExtendClass : BaseClass
{
}
So actually the property Request will always be set with the name of ExtendClass. So it actually will be Request="ExtendClass"
I have many class who extend BaseClass. I know I just can pass string to it, but is that possible to do it?
You can use object.GetType which will always return the type on top in the hierarchy (so the last deriving class):
public string Request
{
get
{
return this.GetType().Name;
}
}
Name will return the short type name, without the namespace. If you want that too, you should use FullName.
You have multiple choices. For example, you can use reflections:
public string Request { get {return this.GetType().Name; }}
Or you can make it more explicit, with abstract property (and that way you can specify not only class names):
public abstract class BaseClass
{
public abstract string Request { get; }
}
class ExtendClass : BaseClass
{
public override string Request { get {return "ExtendClass"; } }
}
For a current project I am trying to make have a generic XML creator using the XmlSerializer class - I need a certain element in one of the classes to have the ElementName set based on the type of class that the creator is in in this context that is simple enough. Here is an example:
public abstract class ElementTypeBase
{
public abstract string ElementName { get; }
}
public class ElementTypeA : ElementTypeBase
{
public override string ElementName
{
get { return "ElementA"; }
}
}
Then pass this to my XML object class which will be used for the XmlSerializer but I want the ElementName to be specific to the type.
public class XMLObject<T> where T : ElementTypeBase
{
[XmlElement(Activator.CreateInstance<T>().ElementName)]
public string SomeElement;
}
I thought I would be able to do that but get:
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
So I thought that I could override ToString() but this doesn't work, I was thinking of using constants but it feels dirty. Any other suggestions?
You can do that through the XmlAttributeOverrides class like shown below.
However, you'll need to cache the XmlSerializer instance and I wouldn't recommend this approach for your problem.
when you say that you have one element that is gonna be different for each inherited object, I'd suggest to put this element in the inherited class, not the generic one, and hard-code its XmlElement.ElementName.
using System;
using System.Xml.Serialization;
public class Program
{
static void Main(string[] args)
{
XmlSerializer serializer =
new XmlSerializer(typeof(XMLObject<MyElement>),
XMLObject<MyElement>.Overrides);
serializer.Serialize(Console.Out,
new XMLObject<MyElement>() { SomeElement = "value" });
Console.ReadLine();
}
}
public class XMLObject<T> where T : ElementTypeBase, new()
{
public static XmlAttributeOverrides Overrides { get; private set; }
static XMLObject()
{
Overrides = new XmlAttributeOverrides();
Overrides.Add(typeof(XMLObject<T>), "SomeElement",
new XmlAttributes
{
XmlElements =
{
new XmlElementAttribute(new T().ElementName)
}
});
}
public string SomeElement { get; set; }
}
public abstract class ElementTypeBase
{
public abstract string ElementName { get; }
}
public class MyElement : ElementTypeBase
{
public override string ElementName
{
get { return "ElementA"; }
}
}
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
}