I have a class has a property need to be serialized with some specified type.
[Serializable]
public class MyClass
{
private object _data;
//[Any magic attribute here to exclude some types while serialization?]
public object Data
{
get { return _data;}
set { _data = value; }
}
}
[Serializable]
public class A
{}
[Serializable]
public class B
{}
MyClass myClass = new MyClass();
In some cases I have:
myClass.Data = A;
In some cases I have:
myClass.Data = B;
And then I do serialization for MyClass.
My question is: how can I serialize class B but class A as Data property inside MyClass?
Thank you for helping me!
You are looking for the ShouldSerialize Pattern:
[XmlRoot(ElementName="Foo")]
public class Foo
{
[XmlElement(ElementName="Bar")]
public int Bar { get; set; }
public bool ShouldSerializeBar()
{
return (Bar > 10);
}
}
The property 'Bar' will be serialized if it's greater than 10, otherwise, it won't.
Just create a boolean method with 'ShouldSerialize' in front of your property name. If the boolean then returns false, the property wil not be serialized.
More specific to your situation:
[XmlInclude(typeof(Foo))]
[XmlInclude(typeof(Bar))]
[XmlRoot(ElementName = "Foo")]
public class FooBar
{
[XmlElement(ElementName = "Data")]
public object Data { get; set; }
public bool ShouldSerializeData()
{
return (Data.GetType() == typeof(Foo));
}
}
[XmlRoot(ElementName="Foo")]
public class Foo
{
[XmlElement(ElementName="Bar")]
public int Bar { get; set; }
}
[XmlRoot(ElementName = "Bar")]
public class Bar
{
[XmlElement(ElementName = "Foo")]
public int Foo { get; set; }
}
Try
[XmlIgnore] attribute class
namespace System.Xml.Serialization
use XmlSerializer Class to serialize
I think that you are looking for this - http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributes.xmlignore(v=vs.110).aspx
You can add [XmlIgnoreAttribute] attributte to your Data property, and optionally (if (Data is B) ..) remove it with newly created XmlAttributes. Then you use new XmlSerializer with edited behaviour.
Edit: Example:
public class A
{
public string Text { get; set; }
}
public class B : A { }
public class C : A { }
[XmlInclude(typeof(B))]
[XmlInclude(typeof(C))]
public class D
{
public object Data { get; set; }
public string Test = "Test";
}
class Program
{
static void Main(string[] args)
{
var d = new D();
d.Data = new B() { Text = "Data" };
var xSer = CreateOverrider(d);
xSer.Serialize(new StreamWriter(File.OpenWrite("D:\\testB.xml")), d);
}
public static XmlSerializer CreateOverrider(D d)
{
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
attrs.XmlIgnore = d.Data is B;
xOver.Add(typeof(D), "Data", attrs);
XmlSerializer xSer = new XmlSerializer(typeof(D), xOver);
return xSer;
}
}
For B you get:
<?xml version="1.0" encoding="utf-8"?>
<D xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Test>Test</Test>
</D>
For C:
<?xml version="1.0" encoding="utf-8"?>
<D xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Test>Test</Test>
<Data xsi:type="C">
<Text>Data</Text>
</Data>
</D>
Related
So I have searched all I can, but cannot find the exact problem I am encountering.
This is my nested XML:
<Message>
<Foo>
<Bar>1</Bar>
<Baz>2</Baz>
<Qux>3</Qux>
</Foo>
</Message>
And I have a class in C#:
[Serializable()]
[XmlRoot("Message")]
public class Foo
{
[XmlElement("Bar")]
public string Bar { get; set; }
[XmlElement("Baz")]
public string Baz { get; set; }
[XmlElement("Qux")]
public string Qux { get; set; }
}
Now Message is just arbitrary and gets sent with every XML message. So every XML message sent will have a <Message> tag around it. When I put Foo as the XmlRoot it throws an error, and with Message as XmlRoot it doesn't recognize the child elements. I am looking for a clean and easy solution to this. Thanks!
I haven't tested this, but it should work.
[Serializable()]
[XmlRoot("Message")]
public class Message
{
[XmlElement("Foo")]
public Foo Foo { get; set; }
}
[Serializable()]
public class Foo
{
[XmlElement("Bar")]
public string Bar { get; set; }
[XmlElement("Baz")]
public string Baz { get; set; }
[XmlElement("Qux")]
public string Qux { get; set; }
}
Use to get correct model here link
After that you can these methods to serialize and deserialize:
public static class XMLFactory
{
public static T XmlDeserializeFromString<T>(this string objectData)
{
return (T)XmlDeserializeFromString(objectData, typeof(T));
}
public static object XmlDeserializeFromString(this string objectData, Type type)
{
try
{
var serializer = new XmlSerializer(type);
object result;
using (TextReader reader = new StringReader(objectData))
{
result = serializer.Deserialize(reader);
}
return result;
}
catch (Exception ex)
{
LoggerHelper.LogError(ex.ToString());
return null;
}
}
public static string XmlSerializeToString(this object objectInstance)
{
var serializer = new XmlSerializer(objectInstance.GetType());
var sb = new StringBuilder();
using (TextWriter writer = new StringWriter(sb))
{
serializer.Serialize(writer, objectInstance);
}
return sb.ToString();
}
}
I want to use XAML code to serialise object structure with XamlWriter and also to have possibility that XAML code again with XamlReader to load back in Object structure. Output XAML Code should be readable so that person can edit it(or write new one) and should look something like this:
<?xml version="1.0" encoding="utf-8" ?>
<MainClass>
<Class2 Name="Calss2Name">
<Class3 Name="Class3Name">
</Class3>
</Class2>
<!-- Where Class2Name is reference to Class2 tag and after loading into Object structure Class1 schould have a reference set to Class2-->
<Class1 Class2Property="Calss2Name" />
</MainClass>
and Class structure should look like this:
public class MainClass
{
public MainClass()
{
_class2ListProperty = new List<Class2>();
_class1ListProperty = new List<Class1>();
}
private List<Class2> _class2ListProperty = new List<Class2>();
private List<Class1> _class1ListProperty = new List<Class1>();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Class2> Class2ListProperty
{
get
{
return _class2ListProperty;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Class1> Class1ListProperty
{
get
{
return _class1ListProperty;
}
}
}
public class Class1
{
private Class2 _class2Member;
public String Name { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Class2 Class2Property
{
get
{
return _class2Member;
}
set
{
this._class2Member = value;
}
}
}
public class Class2
{
public Class2()
{
_class3ListMember = new List<Class3>();
}
private List<Class3> _class3ListMember = new List<Class3>();
public String Name { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Class3> Class3ListProperty
{
get
{
return _class3ListMember;
}
}
}
public class Class3
{
public Class3()
{
_class3ListMember = new List<Class3>();
}
public String Name { get; set; }
[TypeConverter(typeof(Int32RangeConverter))]
public Int32Range Runtime { get; set; }
private List<Class3> _class3ListMember = new List<Class3>();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Class3> Class3ListProperty
{
get
{
return _class3ListMember;
}
}
}
Is it posssible to build such structure boht for XAML File and for Classes without putting whole Class2 node once more in Class1 tree? When yes, I will appreciate any suggestions.
Thanks in advance.
I've made it working! Using MarkupExtension I could reference Property to correspondig object. So, Xaml code should look like this:
<?xml version="1.0" encoding="utf-8" ?>
<MainClass>
<Class2 Name="Calss2Name">
<Class3 Name="Class3Name">
</Class3>
</Class2>
<!-- Where Class2Name is reference to Class2 tag and after loading into Object structure Class1 schould have a reference set to Class2-->
<Class1 Class2Property="{Class2MarkupExtension class2Name=Calss2Name}" />
</MainClass>
and I have to implement Class2MarkupExtension Class which inherit class MarkupExtension:
[MarkupExtensionReturnType(typeof(Class2))]
public class Class2MarkupExtension : MarkupExtension
{
#region Properties
[ConstructorArgument("class2Name")]
public String Class2Name { get; set; }
#endregion
#region Constructors
public Class2MarkupExtension()
{
}
public Class2MarkupExtension(String class2Name)
{
Class2Name = class2Name;
}
#endregion
#region Methods
public override object ProvideValue(IServiceProvider provider)
{
Type providerType = provider.GetType();
///Get _xamlContext member from provider.
FieldInfo fieldsInfo = providerType.GetField("_xamlContext", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
object parserContex = fieldsInfo.GetValue(provider);
///Get _rootInstance member from _xamlContext member.
providerType = parserContex.GetType();
fieldsInfo = providerType.GetField("_rootInstance", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
Class2 class2 = null;
MainClass rootElement = (MainClass)fieldsInfo.GetValue(parserContex);
if (rootElement.Class2ListProperty != null && rootElement.Class2ListProperty.Count > 0)
{
class2 = rootElement.Class2ListProperty .Where(s => s.Name.Equals(Class2Name)).FirstOrDefault();
}
if (scenario == null)
{
class2 = new Class2() { Name = Class2Name };
}
return class2;
}
#endregion
}
further I've created LoadMainClass method which converts Xaml into Object structure:
public class MainClassFactory
{
/// <summary>
/// Returns MainClass Object from Xaml Code.
/// </summary>
public static MainClass LoadMainClass(String xamlAsString)
{
StringReader stringReader = new StringReader(xamlAsString.ToString());
XmlReader xmlReader = XmlReader.Create(stringReader);
object outputMainClass = XamlReader.Load(xmlReader);
return outputMainClass as MainClass;
}
}
This is solution which works on .Net Framework 3.5 for .Net Framework 4+ is slightly different, you don't have to use reflection to get _rootInstance field just have to do this:
IRootObjectProvider rootProvider = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
I am having huge problems with XML serialization. I have two classes, both need to be serializeable. In the inherited class, I would like to change the serialization behavior, so that a string property gets serialized as complex type.
public class Animal
{
[XmlElement(ElementName = "NAME")]
public string Name { get; set; }
public virtual bool ShouldSerializeName() { return true; }
}
public class Cat : Animal
{
public override bool ShouldSerializeName() { return false; }
[XmlElement(ElementName = "NAME")]
public NameAndType Name2 { get; set; }
}
public class NameAndType
{
public string Name { get; set; }
public string Type { get; set; }
}
...
var cat = new Cat {Name2 = new NameAndType {Name = "LittleCat"}};
new XmlSerializer(typeof(Cat)).Serialize(Console.Out, cat);
I have tried different approaches, but I didn't find a way to change how the NAME element get's serialized.
With the example above, I get the error message:
The XML element 'NAME' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.
The reason you get the error is that, during XmlSerializer code generation, the code generator doesn't understand that the two potential NAME elements on Cat will never be simultaneously serialized, so throws the exception.
Instead, you can apply XmlAnyElementAttribute to a virtual property returning an XElement, then manually create and return an appropriate XElement for the name for each class in the hierarchy:
[XmlInclude(typeof(Cat))]
public class Animal
{
[XmlIgnore]
public string Name { get; set; }
[XmlAnyElement]
public virtual XElement XmlName
{
get
{
return Name == null ? null : new XElement("NAME", Name);
}
set
{
Name = (value == null ? null : value.Value);
}
}
}
public class Cat : Animal
{
// Must be cached as per https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.110%29.aspx
static XmlSerializer nameSerializer;
static Cat()
{
nameSerializer = new XmlSerializer(typeof(NameAndType), new XmlRootAttribute("NAME"));
}
[XmlIgnore]
public NameAndType Name2 { get; set; }
[XmlAnyElement]
public override XElement XmlName
{
get
{
return (Name2 == null ? null : XObjectExtensions.SerializeToXElement(Name2, nameSerializer, true));
}
set
{
Name2 = (value == null ? null : XObjectExtensions.Deserialize<NameAndType>(value, nameSerializer));
}
}
}
Using the extension methods:
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element)
{
return element.Deserialize<T>(new XmlSerializer(typeof(T)));
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
object result = serializer.Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static XElement SerializeToXElement<T>(this T obj)
{
return obj.SerializeToXElement(new XmlSerializer(obj.GetType()), true);
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
(ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
serializer.Serialize(writer, obj, ns);
}
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
}
Which, for a List<Animal>, produces XML like this:
<ArrayOfAnimal xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Animal>
<NAME>duck</NAME>
</Animal>
<Animal xsi:type="Cat">
<NAME>
<Name>Smokey</Name>
<Type>Siamese</Type>
</NAME>
</Animal>
</ArrayOfAnimal>
You're specifying two different XML elements with the same name on the same graph level, which is not allowed.
I will offer a solution, but it involves concatenating type and name for the Cat class serialization. If it is not imperative to serialize that NameAndType class, then go on:
On Animal, set Name to be virtual;
On Cat, set XmlIgnore on Name2. Then override Name and return both properties of Name2 the way you fancy.
If you really need to serialize that class as it is, then I'm afraid you'll have to so it with a different elementName.
Edit: Sample code:
public class Animal
{
[XmlElement(ElementName = "NAME")]
public virtual string Name { get; set; }
public virtual bool ShouldSerializeName() { return true; }
}
public class Cat : Animal
{
public override bool ShouldSerializeName() { return false; }
[XmlIgnore]
public NameAndType Name2 { get; set; }
[XmlElement(ElementName = "NAME")]
public override string Name
{
get
{
return String.Format("{0} [Type:{1}]", Name2.Name, Name2.Type);
}
set { }
}
}
I have a problem about deserialize an array. Becouse array elements can be of various types. You can see the example:
<?xml version="1.0" encoding="UTF-8"?><export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://zakupki.gov.ru/oos/export/1" xmlns:oos="http://zakupki.gov.ru/oos/types/1">
<notificationZK>
... item 1 data
</notificationZK>
<notificationZK>
... item 2 data
</notificationZK>
<notificationFF>
... item 3 data
</notificationFF>
</export>
All elements extends notificationType
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationSZType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationPOType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationZKType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationEFType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationOKType))]
public partial class notificationType
{
...
So the question is how can I get the collection of notificationType elements from my XML file? I think I cant do something like
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlArray("")] // ???? what I need write here?
[XmlArrayItem("", typeof(notificationType))] // ??? and here?
public notificationType[] notification { get; set; }
}
Regards!
ADDED-------------
So. I make this:
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
class Program
{
static void Main(string[] args)
{
NotificationCollection collection = null;
string path = #"E:\notification.xml";
XmlSerializer serializer = new XmlSerializer(typeof(notificationType));
StreamReader reader = new StreamReader(path);
collection = (NotificationCollection) serializer.Deserialize(reader);
reader.Close();
}
}
but have System.InvalidOperationException was unhandled while serializer.Deserialize(reader);
Message=<export xmlns='http://zakupki.gov.ru/oos/export/1'> not expected.
What im doing wrong?
How about moving the type declarations into the collection?
[XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationZK", typeof(NotificationTypeZK))]
[XmlElement("notificationFF", typeof(NotificationTypeFF))]
public List<NotificationType> Notifications { get; set; }
}
public class NotificationType
{
}
public class NotificationTypeZK : NotificationType { }
public class NotificationTypeFF : NotificationType { }
static void Main(string[] args)
{
var data = #"<export><notificationZK /><notificationZK /><notificationFF /></export>";
var serializer = new XmlSerializer(typeof(NotificationCollection));
using (var reader = new StringReader(data))
{
var notifications = serializer.Deserialize(reader);
}
}
This should do the job
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
This question is interesting for me as well. I wrote the simplified app to achieve what you ask for:
[Serializable]
[XmlInclude(typeof(ItemA))]
[XmlInclude(typeof(ItemB))]
public class BaseItem
{
public bool Value { get; set; }
}
[Serializable]
public class ItemA : BaseItem
{
public string Text { get; set; }
}
[Serializable]
public class ItemB : BaseItem
{
public int Number { get; set; }
}
[Serializable]
public class ItemsArray
{
public BaseItem[] Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var array = new ItemsArray
{
Items = new BaseItem[]
{
new ItemA { Value = true, Text = "Test" },
new ItemB { Value = false, Number = 7 }
}
};
ItemsArray output;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(ItemsArray));
serializer.Serialize(stream, array);
stream.Position = 0;
output = (ItemsArray)serializer.Deserialize(stream);
}
}
}
After deserialization we get exactly what we serialized. The XML inside stream looks like:
<?xml version="1.0"?>
<ItemsArray xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem xsi:type="ItemA">
<Value>true</Value>
<Text>Test</Text>
</BaseItem>
<BaseItem xsi:type="ItemB">
<Value>false</Value>
<Number>7</Number>
</BaseItem>
</Items>
</ItemsArray>
As was mentioned in other answer, you can't use different tags inside XML array. However, it's still possible to store different types. Serializer does this by using xsi:type attribute.
In order to solve your problem you probably need to use a bit another scheme of XML.
If I have the following:
public class A
{
public B b {get;set;}
}
public class B
{
public string Name {get;set;}
public string Address {get;set;
}
what I want is the xml as:
<A Name="some data" Address="address..." />
So I am trying to flatten my referenced object as attributes.
Is this possible with an XmlSerializer?
yeah, you can do this by using the IXmlSerializable interface:
[Serializable]
public class MyClass : IXmlSerializable
{
public MySubClass SubClass { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartAttribute("Name");
writer.WriteString(SubClass.Name);
writer.WriteEndAttribute();
writer.WriteStartAttribute("Phone");
writer.WriteString(SubClass.Phone);
writer.WriteEndAttribute();
}
}
[Serializable]
public class MySubClass
{
public string Name { get; set; }
public string Phone { get; set; }
}
and then call it like this
var serializer = new XmlSerializer(typeof(MyClass));
using (var writer = new StringWriter())
{
var myClass = new MyClass() {SubClass = new MySubClass() {Name = "Test", Phone = "1234"}};
serializer.Serialize(writer, myClass);
string xml = writer.ToString();
}
this is the xml result:
<?xml version="1.0" encoding="utf-16"?>
<MyClass Name="Test" Phone="1234" />
see msdn too: http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
or you could just specify the attributes that #Morpheus named ;)