Question
Is it possible to skip invalid values up on de-serialization? For example if a user inserted a invalid value inside the xml file.
Class Definition
using Relink.Data.Enum;
using System;
using System.IO;
using System.Xml.Serialization;
using System.ComponentModel;
namespace Relink {
[Serializable]
public class Settings {
internal static XmlSerializer Serializer = new XmlSerializer(typeof(Settings));
public Difficulty Difficulty {
get;
set;
}
public Boolean CaptureMouse {
get;
set;
}
internal void loadDefaults() {
this.Difficulty = Difficulty.Normal;
this.CaptureMouse = false;
}
}
}
Serialization Method
// ...
if(!File.Exists(GameDir + SettingsFile)) {
Settings = new Settings();
Settings.loadDefaults();
TextWriter writer = new StreamWriter(GameDir + SettingsFile);
Settings.Serializer.Serialize(writer, Settings);
writer.Close();
writer.Dispose();
} else {
TextReader reader = new StreamReader(GameDir + SettingsFile);
Settings = (Settings)Settings.Serializer.Deserialize(reader);
}
// ...
XML Content (valid)
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Difficulty>Normal</Difficulty>
<CaptureMouse>false</CaptureMouse>
</Settings>
XML Content (invalid)
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Difficulty>Moo</Difficulty>
<CaptureMouse>false</CaptureMouse>
</Settings>
Remarks
I don't want to "reset" the users settings, i just want to skip the invalid stuff and use default values instead. Otherwise i would you a try/catch construct and just re-generate the xml file.
Unfortunately there is no way to suppress the exception inside XmlSerializer when an unknown enum value is encountered. Instead, you will need to create a string-valued property for this purpose, and serialize that instead of the enum-valued property:
[Serializable]
public class Settings
{
internal static XmlSerializer Serializer = new XmlSerializer(typeof(Settings));
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[XmlElement("Difficulty")]
public string XmlDifficulty
{
get
{
return Difficulty.ToString();
}
set
{
try
{
Difficulty = (Difficulty)Enum.Parse(typeof(Difficulty), value);
}
catch
{
Debug.WriteLine("Invalid difficulty found: " + value);
Difficulty = Difficulty.Normal;
}
}
}
[XmlIgnore]
public Difficulty Difficulty { get; set; }
public Boolean CaptureMouse { get; set; }
internal void loadDefaults()
{
this.Difficulty = Difficulty.Normal;
this.CaptureMouse = false;
}
}
Related
I'm using the ShouldSerialize method to conditionally serialize a field when I use XmlSerializer. But now I need to serialize the same class using JSON, but I would like the ShoulSerialize method to be ignored when using JSON, can I do this in any way?
[TestClass]
public class UnitTest1
{
private readonly Customer _customer = new Customer
{
FirstName = "Paulo",
LastName = "Balbino",
Age = 25
};
[Serializable]
public class Customer
{
[XmlElement("FirstName")]
public string FirstName { get; set; }
[XmlElement("LastName")]
public string LastName { get; set; }
[XmlElement("Age")]
public int Age { get; set; }
public bool ShouldSerializeLastName()
{
return Age > 30; // Enter here only if it is XmlSerialize.
}
}
[TestMethod]
public void XmlSerialize()
{
try
{
var xmlSerializer = new XmlSerializer(typeof(Customer));
using (var textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, _customer);
Debug.WriteLine(textWriter.ToString());
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
[TestMethod]
public void JsonConvert()
{
try
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(_customer);
Debug.WriteLine(json);
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
}
Result Xml:
<?xml version="1.0" encoding="utf-16"?>
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>Paulo</FirstName>
<Age>25</Age>
</Customer>
Result Json:
{"FirstName":"Paulo","Age":25}
Both go to the ShouldSerializeLastName() method, however, I'd like it to be ignored in JSON and all fields would be displayed.
You can disable Json.NET's support for ShouldSerialize*() conditional serialization by using your own contract resolver and setting DefaultContractResolver.IgnoreShouldSerializeMembers = true.
First, allocate a contract resolver as follows:
static DefaultContractResolver IgnoreShouldSerializeContractResolver = new DefaultContractResolver
{
IgnoreShouldSerializeMembers = true,
//Set other properties as required, e.g.:
//NamingStrategy = new CamelCaseNamingStrategy(),
};
Then use it as follows:
var settings = new JsonSerializerSettings
{
ContractResolver = IgnoreShouldSerializeContractResolver,
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(_customer, settings);
Notes:
You may want to cache the contract for best performance, as it generates contract information for each type only once, then caches it.
Because the contract resolver caches type information, modifying properties of a pre-existing contract resolver after it has been constructed and used to generate contracts is not recommended.
IgnoreShouldSerializeMembers was introduced in Json.NET 11.0.1.
Sample fiddle here.
can somebody help with this problem. I have created class for deserialization data from XML. But when I programme compile VS shows me this exception InvalidOperationException: There is an error in XML document(2,2) so I guess that I defined attribute Month in class Store. I tried to type into int and without successfull please help me... Here is the code of problem:
<?xml version="1.0" encoding="utf-8" ?>
<Store>
<StoreS Month="2">
<Amount>159</Amount>
<Mod_date> 20.3.2014 18:19:18</Mod_date>
</StoreS>
<StoreS Month="2">
<Amount>270</Amount>
<Mod_date> 20.3.2014 18:19:40</Mod_date>
</StoreS>
</Store>
The class into which I wanna to deserialize data is written this way:
[XmlRoot("Store"),XmlType("Store")]
public class Store
{
[XmlElement("StoreS")]
public List<RecordStore> StoreS = new List<RecordStore>();
[XmlAttribute("Month")]
public string Month { get; set; }
}
public class RecordStore
{
[XmlElement("Amount")]
public int amount{get;set;}
[XmlElement("Mod_date")]
public DateTime mod_date { get; set; }
}
Thank you very much for your help.
Your xml has bad datetime format, it should be like 2014-03-21T00:00:00, for example
as you serialize in the same way you can de-serialize the object
public void Serialize<T>(T details)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextWriter writer = new StreamWriter("Xml.xml"))
{
serializer.Serialize(writer, details);
}
}
public void Deserialize<T>(out T obj)
{
XmlSerializer serializer = new XmlSerializer(typeof (T));
using (TextReader reader = new StreamReader("Xml.xml"))
{
obj = (T)serializer.Deserialize(reader);
}
}
I have a class which contain an interface member variable.How can i deserialize this class
interface ISensor { }
[Serializable]
class Sensor: ISensor { }
[Serializable]
class Root
{
[XmlElement("Sensor")]
public List<ISensor> SensorList{ get; set; }
}
My XML will be like this
<?xml version="1.0" encoding="us-ascii"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Sensor >
<SensorName>Name1</SensorName>
<SensorValue>0.0</SensorValue>
</Sensor>
<Sensor>
<SensorName>Name2</SensorName>
<SensorValue>148.00</SensorValue>
</Sensor>
</Root>
Assuming you are using XmlSerializer, there are two changes required for both serialization and deserialization:
Tell the serializer the list of types that can appear, in other words the types that inherit from Sensor.
Use a class for the List rather than an interface, in other words replace List<ISensor> with List<Sensor>.
Unfortunately, the XmlSerializer does not handle interfaces in the way you want. See XmlSerializer serialize generic List of interface for more information.
If using a base class is not an option You could write your own XML serializer by implementing IXmlSerializable. Override ReadXml and parse the XML manually.
For example:
public interface ISensor { }
[Serializable]
public class Sensor : ISensor { }
[Serializable]
public class Root
{
// Changed List<ISensor> to List<Sensor>. I also changed
// XmlElement to XmlArray so it would appear around the list.
[XmlArray("Sensor")]
public List<Sensor> SensorList { get; set; }
}
[Serializable]
public class SensorA : Sensor
{
[XmlElement("A")]
public string A { get; set; }
}
[Serializable]
public class SensorB : Sensor
{
[XmlElement("B")]
public string B { get; set; }
}
class Program
{
public static void Main(string[] args)
{
XmlSerializer xmlSerializer;
Root root = new Root();
root.SensorList = new List<Sensor>();
root.SensorList.Add(new SensorA() {A = "foo"});
root.SensorList.Add(new SensorB() {B = "bar"});
// Tell the serializer about derived types
xmlSerializer = new XmlSerializer(typeof (Root),
new Type[]{typeof (SensorA), typeof(SensorB)});
StringBuilder stringBuilder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, root);
}
// Output the serialized XML
Console.WriteLine(stringBuilder.ToString());
Root root2;
using (StringReader stringReader = new StringReader(stringBuilder.ToString()))
{
root2 = (Root) xmlSerializer.Deserialize(stringReader);
}
}
}
The output from the Console.WriteLine statement is:
<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Sensor>
<Sensor xsi:type="SensorA">
<A>foo</A>
</Sensor>
<Sensor xsi:type="SensorB">
<B>bar</B>
</Sensor>
</Sensor>
</Root>
How can I change XML-element name for field inherited from base class while doing serialization?
For example I have next base class:
public class One
{
public int OneField;
}
Serialization code:
static void Main()
{
One test = new One { OneField = 1 };
var serializer = new XmlSerializer(typeof (One));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
I get what I need:
<?xml version="1.0" encoding="utf-8"?>
<One xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
</One>
Now I have created new class inherited from A with additional field and custom XML element name for it:
public class Two : One
{
[XmlElement("SecondField")]
public int TwoField;
}
Serialization code:
static void Main()
{
Two test = new Two { OneField = 1, TwoField = 2 };
var serializer = new XmlSerializer(typeof (Two));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
As a result I get next output:
<?xml version="1.0" encoding="utf-8"?>
<Two xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
<SecondField>2</SecondField>
</Two>
The problem is that I want to change OneField in this output to FirstField without touching base class code (because I will use it too and the names must be original). How can I accomplish this?
Thank you.
Try this:
public class Two : One
{
private static XmlAttributeOverrides xmlOverrides;
public static XmlAttributeOverrides XmlOverrides
{
get
{
if (xmlOverrides == null)
{
xmlOverrides = new XmlAttributeOverrides();
var attr = new XmlAttributes();
attr.XmlElements.Add(new XmlElementAttribute("FirstField"));
xmlOverrides.Add(typeof(One), "OneField", attr);
}
return xmlOverrides;
}
}
[XmlElement("SecondField")]
public string TwoField;
}
And your serializer init is a lot easier:
var xmls = new System.Xml.Serialization.XmlSerializer(typeof(Two), Two.XmlOverrides);
Here's a workaround: Override the fields in the subclass and mark the overriden field with whatever name you need. For example,
class One
{
public int OneField { get; set; }
}
class Two : One
{
[XmlElement("FirstField")]
public new int OneField
{
get { return base.OneField; }
set { base.OneField = value; }
}
}
I am using VSTS2008 + C# + .Net 3.0. I am using below code to serialize XML, here is my current code and serialized XML file. My purpose is I want to make MyInnerObjectProperties belongs to a special XML namespace (http://foo/2009) and making this namespace as default namespace. Any ideas how to implement this?
Current output:
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<MyInnerObjectProperties>
<MyInnerObjectProperty>
<ObjectName>Foo Type</ObjectName>
</MyInnerObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Goo Type</ObjectName>
</MyInnerObjectProperty>
</MyInnerObjectProperties>
</MyObjectProperty>
</MyClass>
Current code:
public class MyClass
{
private MyObject[] _myObjectProperty;
[XmlElement(IsNullable=false)]
public MyObject[] MyObjectProperty
{
get
{
return _myObjectProperty;
}
set
{
_myObjectProperty = value;
}
}
}
public class MyObject
{
private MyInnerObject[] _myInnerObjectProperty;
[XmlArrayItemAttribute("MyInnerObjectProperty", typeof(MyInnerObject), IsNullable=false)]
public MyInnerObject[] MyInnerObjectProperties
{
get
{
return _myInnerObjectProperty;
}
set
{
_myInnerObjectProperty = value;
}
}
}
public class MyInnerObject
{
public string ObjectName;
}
public class Program
{
static void Main(string[] args)
{
XmlSerializer s = new XmlSerializer(typeof(MyClass));
FileStream fs = new FileStream("foo.xml", FileMode.Create);
MyClass instance = new MyClass();
instance.MyObjectProperty = new MyObject[1];
instance.MyObjectProperty[0] = new MyObject();
instance.MyObjectProperty[0].MyInnerObjectProperties = new MyInnerObject[2];
instance.MyObjectProperty[0].MyInnerObjectProperties[0] = new MyInnerObject();
instance.MyObjectProperty[0].MyInnerObjectProperties[0].ObjectName = "Foo Type";
instance.MyObjectProperty[0].MyInnerObjectProperties[1] = new MyInnerObject();
instance.MyObjectProperty[0].MyInnerObjectProperties[1].ObjectName = "Goo Type";
s.Serialize(fs, instance);
return;
}
}
How about this:
[XmlArrayItemAttribute( Namespace = "http://foo.com/2009" /* other attr. params. */ )]
public MyInnerObject[] MyInnerObjectProperties
{
get { ... }
set { ... }
}
Try
public class MyObject
{
[XmlArrayItemAttribute("MyInnerObjectProperty", typeof (MyInnerObject),
IsNullable = false)]
[XmlArray(Namespace = "http://foo.com/2009")]
public MyInnerObject[] MyInnerObjectProperties { get; set; }
}
for me, this produces:
<?xml version="1.0" encoding="utf-8"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<MyInnerObjectProperties xmlns="http://foo.com/2009">
<MyInnerObjectProperty>
<ObjectName>Foo Type</ObjectName>
</MyInnerObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Goo Type</ObjectName>
</MyInnerObjectProperty>
</MyInnerObjectProperties>
</MyObjectProperty>
</MyClass>
You need to create an XmlSerializerNamespaces object, and add your needed namespaces to it.
The XmlSerializerNamespaces object contains the XML namespaces and prefixes that the XmlSerializer uses to generate qualified names in an XML-document instance.
In your c# code:
XmlSerializerNamespaces myNameSpaces = new XmlSerializerNamespaces();
myNameSpaces.Add("MyInnerObject", "http://foo/2009");
Then, add an attribute to your class, like this:
public class MyInnerObject
{
[XmlElement(Namespace = "http://foo/2009")]
More info at:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializernamespaces.aspx