I cannot find a reason why properties without getters aren't parsing properly, let me write you an example:
For XML in format
<request>
<job
mode="modefirst"
/>
<request>
I am trying to deserialize it to the POCO with a property:
private ESomeEnum emode;
[XmlAttribute(AttributeName = "mode")]
public string Mode
{
set { ESomeEnum.TryParse( blah blah );
}
emode is being set for default value in class constructor, while deserializing (System.Xml.Serialization without custom classes, just trying to be minimalistic in here) the xml from above, the setter is never being called, but when property 'Mode' contains a getter
get { return this.emode.ToString(); }
setter is actually being hit and proper value set during deserialization.
Why this situation occurs? Is there any reason behind it?
The XmlSerializer processes properties only, which have public get-set accessors. But you can customize anything by implementing IXmlSerializable:
public class MyXmlSerializableClass : IXmlSerializable
{
private ESomeEnum emode = ESomeEnum.modefirst;
public string Mode
{
set { emode = ESomeEnum.Parse(value); }
}
public int ReadWriteProperty { get; set; }
public int SemiReadOnlyProperty { get; private set; }
private int backingFieldOfRealReadOnlyProperty;
public int RealReadOnlyProperty
{
get { return backingFieldOfRealReadOnlyProperty; }
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
if (reader.Settings != null && !reader.Settings.IgnoreWhitespace)
{
reader = XmlReader.Create(reader, new XmlReaderSettings { IgnoreWhitespace = true });
reader.Read();
}
reader.ReadStartElement();
Mode = reader.ReadElementContentAsString("Mode", String.Empty);
ReadWriteProperty = reader.ReadElementContentAsInt("ReadWriteProperty", String.Empty);
SemiReadOnlyProperty = reader.ReadElementContentAsInt("ReadOnlyAutoProperty", String.Empty);
backingFieldOfRealReadOnlyProperty = reader.ReadElementContentAsInt("ReadOnlyProperty", String.Empty);
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("Mode", emode.ToString());
writer.WriteElementString("ReadWriteProperty", ReadWriteProperty.ToString(CultureInfo.InvariantCulture));
writer.WriteElementString("ReadOnlyAutoProperty", SemiReadOnlyProperty.ToString(CultureInfo.InvariantCulture));
writer.WriteElementString("ReadOnlyProperty", RealReadOnlyProperty.ToString(CultureInfo.InvariantCulture));
}
#endregion
internal MyXmlSerializableClass()
{/*needed for deserialization*/
}
}
Related
I have a class implementing IXmlSerializable. This class contains a few properties. Serializing and Deserializing a single instance of the class works fine. But in case of collection of the class, Serialization works fine but Deserialization runs forever. Here is a code snippet. I am using .Net 4.6.2.
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
this.A = Convert.ToInt32(reader.GetAttribute("A"));
this.B = Convert.ToInt32(reader.GetAttribute("B"));
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", this.A.ToString());
writer.WriteAttributeString("B", this.B.ToString());
}
}
class Program
{
static void Main(string[] args)
{
var instance = new MyClass { A = 1, B = 2 };
Serialize(instance);
instance = Deserialize<MyClass>();//works fine
var list = new List<MyClass> { new MyClass { A = 10, B = 20 } };
Serialize(list);
list = Deserialize<List<MyClass>>();//runs forever
}
private static void Serialize(object o)
{
XmlSerializer ser = new XmlSerializer(o.GetType());
using (TextWriter writer = new StreamWriter("xml.xml", false, Encoding.UTF8))
{
ser.Serialize(writer, o);
}
}
private static T Deserialize<T>()
{
XmlSerializer ser = new XmlSerializer(typeof(T));
using (TextReader reader = new StreamReader("xml.xml"))
{
return (T)ser.Deserialize(reader);
}
}
}
Your problem is that, as explained in the documentation, ReadXml() must consume its wrapper element as well as its contents:
The ReadXml method must reconstitute your object using the information that was written by the WriteXml method.
When this method is called, the reader is positioned on the start tag that wraps the information for your type. That is, directly on the start tag that indicates the beginning of a serialized object. When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
MyClass.ReadXml() is not doing this, which causes an infinite loop when the MyClass object is not serialized as the root element. Instead, your MyClass must look something like this:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
/*
* https://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.readxml.aspx
*
* When this method is called, the reader is positioned at the start of the element that wraps the information for your type.
* That is, just before the start tag that indicates the beginning of a serialized object. When this method returns,
* it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method,
* the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these
* positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
*/
var isEmptyElement = reader.IsEmptyElement;
this.A = XmlConvert.ToInt32(reader.GetAttribute("A"));
this.B = XmlConvert.ToInt32(reader.GetAttribute("B"));
reader.ReadStartElement();
if (!isEmptyElement)
{
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
Now your <MyClass> element is very simple with no nested or optional elements. For more complex custom serializations there are a couple of strategies you could adopt to guarantee that your ReadXml() method reads exactly as much as it should, no more and no less.
Firstly, you could call XNode.ReadFrom() to load the current element into an XElement. This requires a bit more memory than parsing directly from an XmlReader but is much easier to work with:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
var element = (XElement)XNode.ReadFrom(reader);
this.A = (int)element.Attribute("A");
this.B = (int)element.Attribute("B");
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
Secondly, you could use XmlReader.ReadSubtree() to ensure the required XML content is consumed:
public class MyClass : IXmlSerializable
{
public int A { get; set; }
public int B { get; set; }
public XmlSchema GetSchema()
{
return null;
}
protected virtual void ReadXmlSubtree(XmlReader reader)
{
this.A = XmlConvert.ToInt32(reader.GetAttribute("A"));
this.B = XmlConvert.ToInt32(reader.GetAttribute("B"));
}
public void ReadXml(XmlReader reader)
{
// Consume all child nodes of the current element using ReadSubtree()
using (var subReader = reader.ReadSubtree())
{
subReader.MoveToContent();
ReadXmlSubtree(subReader);
}
reader.Read(); // Consume the end element itself.
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("A", XmlConvert.ToString(this.A));
writer.WriteAttributeString("B", XmlConvert.ToString(this.B));
}
}
A few final notes:
Be sure to handle both <MyClass /> and <MyClass></MyClass>. These two forms are semantically identical and a sending system could chose either.
Prefer the methods from the XmlConvert class to convert primitives from and to XML. Doing so handles internationalization correctly.
Be sure to test with and without indentation. Sometimes a ReadXml() method will consume an extra XML node but the bug will be hidden when indentation is enabled -- as it is the whitespace node that gets eaten.
For further reading see How to Implement IXmlSerializable Correctly.
I have a problem with implementing an IsDirty mechanism with my XmlSerializer system.
This is how my serialization is called:
public OCLMEditorModel()
{
DeSerialize();
}
public void Serialize()
{
XmlSerializer x = new XmlSerializer(_ModelData.GetType());
using (StreamWriter writer = new StreamWriter(_strPathModelDataXml))
{
x.Serialize(writer, _ModelData);
}
}
public void DeSerialize()
{
_ModelData = new OCLMModelData();
XmlSerializer x = new XmlSerializer(_ModelData.GetType());
using (StreamReader reader = new StreamReader(_strPathModelDataXml))
{
_ModelData = (OCLMModelData)x.Deserialize(reader);
}
}
It reads and saves perfectly, no issues there. But it is the IsDirty flag I have issues with. Directly after the DeSerialize call ...
Ass the IsDirty are set to true. Even though all we have done is read it in from the computer. Example properties:
public class MaterialItem
{
[XmlAttribute]
public string Setting
{
get { return _Setting; }
set
{
_Setting = value;
MarkDirty();
}
}
private string _Setting;
[XmlText]
public string Material
{
get { return _Material; }
set
{
_Material = value;
MarkDirty();
}
}
private string _Material;
[XmlIgnore]
public bool IsDirty { get { return _isDirty; } }
private bool _isDirty;
public void MarkClean()
{
_isDirty = false;
}
protected void MarkDirty()
{
_isDirty = true;
}
public MaterialItem()
{
MarkClean();
}
}
Ideally, the flag should be false when we have just read it using XMLSerializer.
What am I doing wrong?
Thank you.
XmlSerializer doesn't work in any mysterious way.
It uses reflection, yes, but only to get the properties it can serialize/deserialize. Then it uses those properties to get/set the required values.
So during the deserialization those setters will be called, thus calling the MarkDirty method, thus marking your entities dirty.
There isn't anything you can change in XmlSerializer, but you can change your deserialization method, so it sets the entity clean just after deserializing it:
public void DeSerialize()
{
_ModelData = new OCLMModelData();
XmlSerializer x = new XmlSerializer(_ModelData.GetType());
using (StreamReader reader = new StreamReader(_strPathModelDataXml))
{
_ModelData = (OCLMModelData)x.Deserialize(reader);
}
_ModelData.MarkClean();
}
I need to deserialize json for following class.
public class Test
{
public string Property { get; set; }
private Test()
{
//NOTHING TO INITIALIZE
}
public Test(string prop)
{
Property = prop;
}
}
I can create an instance of Test like
var instance = new Test("Instance");
considering my json something like
"{ "Property":"Instance" }"
How shall I create an object of Test class as my default constructor is private and I am getting object where Property is NULL
I am using Newtonsoft Json parser.
You can make Json.Net call the private constructor by marking it with a [JsonConstructor] attribute:
[JsonConstructor]
private Test()
{
//NOTHING TO INITIALIZE
}
Note that the serializer will still use the public setters to populate the object after calling the constructor.
Another possible option is to use the ConstructorHandling setting:
JsonSerializerSettings settings = new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
Test t = JsonConvert.DeserializeObject<Test>(json, settings);
It doesn't seem like you need to take any extra steps.
Using Json.NET v6.0.8, the following C# program works inside LINQPad:
void Main()
{
var o = JsonConvert.DeserializeObject<Test>("{\"Property\":\"Instance\"}");
Debug.Assert(o.Property == "Instance",
"Property value not set when deserializing.");
}
public class Test
{
public string Property { get; set; }
private Test()
{
}
public Test(string propertyValue)
{
Property = propertyValue;
}
}
No need to create a Serializer setting and give assign ConstructorHandling here. Please remember to define the [JsonConstructor] attribute to the private constructor.
I have similar case with abstract BaseNode.cs and its concrete ComputerNode.cs implementation. You can create the classes, copy/paste the code below and do some experiment.
public abstract class BaseNode
{
[JsonConstructor] // ctor used when Json Deserializing
protected BaseNode(string Owner, string Name, string Identifier)
{
this.Name = Name;
this.Identifier = Identifier;
}
// ctor called by concrete class.
protected BaseNode(string [] specifications)
{
if (specifications == null)
{
throw new ArgumentNullException();
}
if (specifications.Length == 0)
{
throw new ArgumentException();
}
Name = specifications[0];
Identifier = specifications[1];
}
public string Name{ get; protected set; }
public string Identifier { get; protected set; }
}
public class ComputerNode: BaseNode
{
public string Owner { get; private set; }
[JsonConstructor] // not visible while creating object from outside and only used during Json Deserialization.
private ComputerNode(string Owner, string Name, string Identifier):base(Owner, Name, Identifier)
{
this.Owner = Owner;
}
public ComputerNode(string[] specifications):base(specifications)
{
Owner = specifications[2];
}
}
For JSon Read and Write following code helps -
public class Operation<T>
{
public string path;
public Operation()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "nodes.txt");
if (File.Exists(path) == false)
{
using (File.Create(path))
{
}
}
this.path = path;
}
public void Write(string path, List<T> nodes)
{
var ser = JsonConvert.SerializeObject(nodes, Formatting.Indented);
File.WriteAllText(path, ser);
}
public List<T> Read(string path)
{
var text = File.ReadAllText(path);
var res = JsonConvert.DeserializeObject<List<T>>(text);
return res;
}
}
All the best!
Today the short answer is: Rename the constructor parameter prop to property and your code will work fine.
public class Test
{
public string Property { get; }
public Test(string property)
{
Property = property;
}
}
Console.WriteLine(
JsonConvert.DeserializeObject(new Test("Instance")));
Newtonsoft.Json supports initializing properties using constructor parameters out of the box, without needing to set any additional attributes or changing any settings. The only constraint is that the parameter name needs to be a case insensitive match to the property name.
I discovered today that having a public constructor that takes parameters and no declared unparameterized constructor causes NewtonSoft to attempt to call the public constructor, the only one that it can find, since there is no explicit default constructor, and it cannot apparently find and call the default constructor provided by the framework unless it is the only constructor.
Explicitly declaring a default constructor causes NewtonSoft to call the correct (unparameterized) constructor.
Is it possible to mark a property in base class with some attribute that remains effective in child classes too?
Question might be very specific to Serialization, but I definitely think there can be other uses as well.
Consider the following code:
using System;
using System.IO;
using System.Xml.Serialization;
namespace Code.Without.IDE
{
[Serializable]
public abstract class C1
{
[XmlIgnore]
public abstract bool IsValid_C1 { get; set;}
}
[Serializable]
public class C2 : C1
{
public bool IsValid_C2 { get; set; }
public override bool IsValid_C1 { get; set;}
public C2()
{
IsValid_C1 = true;
IsValid_C2 = false;
}
}
public static class AbstractPropertiesAttributeTest
{
public static void Main(string[] args)
{
C2 c2 = new C2();
using(MemoryStream ms = new MemoryStream())
{
XmlSerializer ser = new XmlSerializer(typeof(C2));
ser.Serialize(ms, c2);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(result);
}
}
}
}
Above code returns:
------ C:\abhi\Code\CSharp\without IDE\AbstractPropertiesAttributeTest.exe
<?xml version="1.0"?>
<C2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<IsValid_C2>false</IsValid_C2>
<IsValid_C1>true</IsValid_C1>
</C2>
------ Process returned 0
I thought IsValid_C1 will be ignored, though it is not so. Is there any way of achieving this other than marking the property as protected?
Edit: A quick code to show that XmlIgnore attibute is being inherited.
http://ideone.com/HH41TE
I don't believe there is a way to inherit the attribute since you override the base class property. You would need to decorate the IsValid_C1 of C2 with XmlIgnore:
[Serializable]
public class C2 : C1
{
public bool IsValid_C2 { get; set; }
[XmlIgnore]
public override bool IsValid_C1 { get; set; }
public C2()
{
IsValid_C1 = true;
IsValid_C2 = false;
}
}
I will offer a different view of this question. Maybe you just used those properties as an example and want to have severall properties to be cascaded. But i think that this may be a good time to think about the inheritance model proposed.
Basicly you can use regular inheritance or think about some Design Pattern, that can not just solve you issue related to the serialization, but may offer you some more "loose coupling" in you application, making it in a more component model and allowwing each class to deal only with what is concern, this way you can re-use lots of stuff and make your life easier.
Based on that thinking im providing you a sample of the Decorator Design Pattern mixed with the Strategy Design Pattern. If i were developing classes like the ones on your sample, this is how i would do it:
/// <summary>
/// The interface for validation strategy (since we are using interface, there is no need for another abstract class)
/// </summary>
public interface IValidation
{
bool IsValid { get; set; }
}
/// <summary>
/// The decorator (it dont need to be abstract) that has the serializable properties
/// </summary>
[Serializable]
public class ValidatableDecorator : IValidation
{
protected IValidation instance;
public ValidatableDecorator()
{
Init();
}
public ValidatableDecorator(IValidation instance)
{
Init();
}
protected virtual void Init() { }
public void Set(IValidation instance)
{
this.instance = instance;
}
[XmlIgnore]
public bool IsValid
{
get
{
return instance.IsValid;
}
set
{
instance.IsValid = value;
}
}
}
Then you need to implement some classes that have the logic of the strategy Pattern, like this:
public class BossValidatorImplementation : IValidation
{
public bool IsValid
{
get
{
return false; ;
}
set
{
throw new InvalidOperationException("I dont allow you to tell me this!");
}
}
}
public class EasyGoingValidator : IValidation
{
public bool IsValid { get; set; }
}
Now that we have the logic separated from the class, we can inherit from the decorators, choosing wich strategy they use to the IsValid field, like this:
public class ChildWithBossValidation : ValidatableDecorator
{
protected ChildWithBossValidation(IValidation instance)
: this()
{
Init();
}
public ChildWithBossValidation()
: base(new BossValidatorImplementation())
{
Init();
}
protected override void Init()
{
Name = "I'm the boss!";
Sallary = 10000d;
}
public string Name { get; set; }
public double Sallary { get; set; }
}
public class ChildWithEasyGoingValidation : ValidatableDecorator
{
public ChildWithEasyGoingValidation()
: base(new EasyGoingValidator())
{
}
protected ChildWithEasyGoingValidation(IValidation instance)
: this()
{
}
protected override void Init()
{
Name = "Do as you please... :) ";
}
public string Name { get; set; }
}
This is the code to show that the solution works:
public static void Main(string[] args)
{
var boos = new ChildWithBossValidation();
var coolGuy = new ChildWithEasyGoingValidation();
using (var ms = new MemoryStream())
{
var ser = new XmlSerializer(boos.GetType());
ser.Serialize(ms, boos);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine("With override");
Console.WriteLine(result);
}
Console.WriteLine("-------------");
using (var ms = new MemoryStream())
{
var ser = new XmlSerializer(coolGuy.GetType());
ser.Serialize(ms, coolGuy);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine("With override");
Console.WriteLine(result);
}
Console.ReadKey();
}
The result is:
{<?xml version="1.0"?>
<ChildWithBossValidation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>I'm the boss!</Name>
<Sallary>10000</Sallary>
</ChildWithBossValidation>-------------------<?xml version="1.0"?>
<ChildWithEasyGoingValidation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Do as you please... :) </Name>
</ChildWithEasyGoingValidation>}
So, maybe this does not answer how to cascade the Attribute in this case (because you can easily do it by creating your own attribute(marking to allow inheritance) and then implementing some code to SerializeXML). This is just another option that can improve overall architecture of solutions using Design Pattern. But this solve this particular issue also :)
A needed behaviour can be achieved using classes XmlAttributeOverrides and XmlAttributes. I've written helper method for XmlSerializer creation:
public static XmlSerializer GetXmlSerializerWithXmlIgnoreFields(Type t)
{
XmlAttributeOverrides xmlOverrides = new XmlAttributeOverrides();
foreach (var prop in t.GetProperties(BindingFlags.Public|BindingFlags.Instance))
{
Attribute xmlIgnoreAttribute = Attribute.GetCustomAttribute(prop, typeof(XmlIgnoreAttribute));
if (xmlIgnoreAttribute == null)
continue;
XmlAttributes xmlAttributes = new XmlAttributes();
xmlAttributes.XmlIgnore = true;
xmlOverrides.Add(t, prop.Name, xmlAttributes);
}
return new XmlSerializer(t, xmlOverrides);
}
The Main method became:
public static void Main(string[] args)
{
C2 c2 = new C2();
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer ser = GetXmlSerializerWithXmlIgnoreFields(typeof(C2));
ser.Serialize(ms, c2);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(result);
}
}
It appears this feature is broken in C#.
You could write an attribute which through reflection will bring down the attributes for you. Pretty straight forward if you understand reflection.
Ok, so I've got a type:
public class MonitorConfiguration
{
private string m_sourcePath;
private string m_targetPath;
public string TargetPath
{
get { return m_targetPath; }
set { m_targetPath = value; }
}
public string SourcePath
{
get { return m_sourcePath; }
set { m_sourcePath = value; }
}
//need a parameterless constructor, just for serialization
private MonitorConfiguration()
{
}
public MonitorConfiguration(string source, string target)
{
m_sourcePath = source;
m_targetPath = target;
}
}
When I serialise and deserialise a list of these, like this
XmlSerializer xs = new XmlSerializer(typeof(List<MonitorConfiguration>));
using (Stream isfStreamOut = isf.OpenFile("Test1.xml", FileMode.Create))
{
xs.Serialize(isfStreamOut, monitoringPaths);
}
using (Stream isfStreamIn = isf.OpenFile("Test1.xml", FileMode.Open))
{
monitoringPaths = xs.Deserialize(isfStreamIn) as List<MonitorConfiguration>;
}
everything works fine.
However, I really want to hide the public setters of the attributes. This prevents them from being serialised by the XML serialiser. So, I implement my own, like this:
Change the class declaration to this:public class MonitorConfiguration : IXmlSerializable
and add these:
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
//make sure we read everything
while (reader.Read())
{
//find the first element we care about...
if (reader.Name == "SourcePath")
{
m_sourcePath = reader.ReadElementString("SourcePath");
m_targetPath = reader.ReadElementString("TargetPath");
// return;
}
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteElementString("SourcePath", m_sourcePath);
writer.WriteElementString("TargetPath", m_targetPath);
}
This seems to work, however, I only ever get the first item from the list out, all the others are forgotten. I've tried with and without the return that's currently commented out. What am I doing wrong here?
It should be noted that this is just a snippet code that illustrates the problem; I'm limited to which XML serialisation technology I'm using my an eternal mechanic.
This CodeProject article explains how to get around a few pitfalls when working with IXmlSerializable.
Specifically, you probably need to call reader.ReadEndElement(); when you've found all your elements in ReadXml (see the section How to Implement ReadXml? in the article).