I am receiving the following error trying to deserialize xml. This produces the error:
XmlSerializer serializer = new XmlSerializer(typeof(PrivateOptionsAPIResponse));
var result = serializer.Deserialize(streamReader);
Exception:
System.InvalidOperationException was caught
Message=There is an error in XML document (0, 0)
InnerException: System.Xml.XmlException
Message=Root element is missing
Source=System.Xml
I am not sure how to correct the problem. Request returns the following XML:
<PrivateOptionsAPIResponse>
<CountiesForPostalCodeResponse>
<Counties>
<County>
<CountyName>PRINCE WILLIAM</CountyName>
<StateCode>VA</StateCode>
</County>
<County>
<CountyName>MANASSAS CITY</CountyName>
<StateCode>VA</StateCode>
</County>
<County>
<CountyName>MANASSAS PARK CITY</CountyName>
<StateCode>VA</StateCode>
</County>
</Counties>
</CountiesForPostalCodeResponse>
</PrivateOptionsAPIResponse>
I used xsd.exe to generate a class. The definition on PrivateOptionsAPIResponse (generated by xsd.exe tool) shows:
public partial class PrivateOptionsAPIResponse {
private object itemField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("CountiesForPostalCodeResponse", typeof(ZipCodeValidationResponse))]
[System.Xml.Serialization.XmlElementAttribute("PlanDetailsForIndividualOrFamilyResponse", typeof(IndividualPlanBenefitResponse))]
[System.Xml.Serialization.XmlElementAttribute("PlansForIndividualOrFamilyResponse", typeof(IndividualPlanQuoteResponse))]
[System.Xml.Serialization.XmlElementAttribute("ProductDetailsForSmallGroupResponse", typeof(SmallGroupProductBenefitResponse))]
[System.Xml.Serialization.XmlElementAttribute("ProductsForSmallGroupResponse", typeof(SmallGroupProductQuoteResponse))]
public object Item {
get {
return this.itemField;
}
set {
this.itemField = value;
}
}
}
If I then browse to ZipCodeValidationResponse definition it shows this:
public partial class ZipCodeValidationResponse {
private CountyType[] countiesField;
/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute("County", IsNullable=false)]
public CountyType[] Counties {
get {
return this.countiesField;
}
set {
this.countiesField = value;
}
}
}
If I then browse definition on CountyType I see this:
public partial class CountyType {
private string countyNameField;
private StateAbbreviationType stateCodeField;
/// <remarks/>
public string CountyName {
get {
return this.countyNameField;
}
set {
this.countyNameField = value;
}
}
/// <remarks/>
public StateAbbreviationType StateCode {
get {
return this.stateCodeField;
}
set {
this.stateCodeField = value;
}
}
}
----------Working solution----------------:
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
string status = ((HttpWebResponse)response).StatusDescription;
if(status == "OK")
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream))
{
var xmlSerializer = new XmlSerializer(typeof(PrivateOptionsAPIResponse));
var privateOptionsAPIResponse = xmlSerializer.Deserialize(reader) as PrivateOptionsAPIResponse;
}
}
}
}
How are you declaring your streamReader? Take a look at its contents and you'll most likely see it's empty or doesn't contain a complete XML document.
First, check whether your XML is in valid form. From the given exception, it seems you are supplying an invalid xml document. Much appreciate if you could post the content (xml) that you are trying to de serialize.
Related
I have a bunch of xml files that I need to deserialize and store in a database.
I found a generic implementation of an XML deserilizer that does a great job. The only inconvenient is that I cannot deserialize the Celsius degree sign.
The following XML samples contain the Celsius degree sign, which I cannot deserialize:
<pt>
<ch>1</ch>
<type>Analog</type>
<chtype>Temperature</chtype>
<chunits>°C</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
or
<pt>
<ch>5</ch>
<type>Analog</type>
<chtype>Wind Direction</chtype>
<chunits>°</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
My Deserializer implementation is the following:
public class XmlConvert
{
public static string SerializeObject<T>(T dataObject)
{
if (dataObject == null)
{
return string.Empty;
}
try
{
using (StringWriter stringWriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringWriter, dataObject);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
return string.Empty;
}
}
public static T DeserializeObject<T>(string xml)
where T : new()
{
if (string.IsNullOrEmpty(xml))
{
return new T();
}
try
{
using (var stringReader = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
}
catch (Exception ex)
{
return new T();
}
}
}
The class I use to convert is the following:
/// <summary>
/// Container for a single data point.
/// </summary>
public class MessageDataPoint
{
/// <summary>
/// Channel number for data point.
/// </summary>
[System.Xml.Serialization.XmlElement("ch")]
public string Ch { get; set; }
/// <summary>
/// Point type (Analog, Flow)
/// </summary>
[System.Xml.Serialization.XmlElement("type")]
public string Type { get; set; }
/// <summary>
///
/// </summary>
[System.Xml.Serialization.XmlElement("chtype")]
public string ChType { get; set; }
/// <summary>
/// Channel units
/// </summary>
[System.Xml.Serialization.XmlElement("chunits")]
public string ChUnits { get; set; }
/// <summary>
/// Point timestamp.
/// </summary>
[System.Xml.Serialization.XmlElement("time")]
public string Time { get; set; }
[System.Xml.Serialization.XmlIgnore]
public DateTime? TimeParsed
{
get
{
DateTime.TryParse(Time, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt);
return dt;
}
private set { }
}
/// <summary>
/// Channel Value
/// </summary>
[System.Xml.Serialization.XmlElement("value")]
public string Value { get; set; }
}
The deserializer implementation is the following:
public async Task<string> GetFileAsStream(string key)
{
var content = string.Empty;
try
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = key
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
{
using (StreamReader reader = new StreamReader(response.ResponseStream))
{
content = await reader.ReadToEndAsync();
var logger = XmlConvert.DeserializeObject<Logger>(content);
// code removed for brevity
}
}
}
}
The result is the following:
<pt>
<ch>1</ch>
<type>Analog</type>
<chtype>Temperature</chtype>
<chunits>�C</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
<pt>
<ch>2</ch>
<type>Analog</type>
<chtype>Wind Chill</chtype>
<chunits>�C</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
Any ideas?
This is a sample file:
<?xml version="1.0" encoding="ISO-8859-15"?>
<logger>
<id>111111</id>
<mobileNumber>12345676</mobileNumber>
<serialNumber>01404</serialNumber>
<siteName>abcdef</siteName>
<siteId>abcdef</siteId>
<maintenanceflag>False</maintenanceflag>
<messages>
<message>
<id>123456789</id>
<number>123456</number>
<dateReceived>2020-05-04 00:02:25.0</dateReceived>
<dateCredited>2020-05-04 00:02:25.0</dateCredited>
<message> <siteId>abcdef</siteId>
<type>data</type>
<RST>2020-01-18 08:50:00</RST>
<RTC>2020-05-03 23:02:24</RTC>
<DST>2020-05-03 22:00:00</DST>
<mode>0</mode>
<SR>600</SR>
<pt>
<ch>1</ch>
<type>Analog</type>
<chtype>Temperature</chtype>
<chunits>°C</chunits>
<time>2020-05-03 23:00:00</time>
<value>0</value>
</pt>
<pt>
<ch>5</ch>
<type>Analog</type>
<chtype>Wind Direction</chtype>
<chunits>°</chunits>
<time>2020-05-03 23:00:00</time>
<value>0</value>
</pt>
</message>
<format>BINARY</format>
<source>FTP</source>
<batteryCondition>123</batteryCondition>
<signalStrength>12</signalStrength>
</message>
</messages>
</logger>
Feed the stream to the serializer. This will greatly simplify the code.
Moreover, you can completely delete your XmlConvert class.
using (GetObjectResponse response = await client.GetObjectAsync(request))
using (var stream = response.ResponseStream)
{
var serializer = new XmlSerializer(typeof(Logger));
var logger = (Logger)serializer.DeserializeObject(stream);
}
But if you think that the XmlConvert class is necessary, for example, it does some extra work, then at least use Stream in it instead of string.
public static T DeserializeObject<T>(Stream stream)
where T : new()
{
try
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stream);
}
catch (Exception ex)
{
return new T();
}
}
I found the solution by looking at the following post on Stackoverflow
Using .NET how to convert ISO 8859-1 encoded text files that contain Latin-1 accented characters to UTF-8
Thanks to Nyerguds and Alexander Haas for pointing me in the right direction
The solution for me was
public async Task<string> GetFileAsStream(string key)
{
var content = string.Empty;
try
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = key
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
{
using (StreamReader reader = new StreamReader(response.ResponseStream, Encoding.GetEncoding("iso-8859-1")))
{
content = await reader.ReadToEndAsync();
var logger = XmlConvert.DeserializeObject<Logger>(content);
// code removed for brevity
}
}
}
}
I am parsing XML, I have generated Class from XSD file, I am able to access simple values from XML, but I don't know how to get value from this. I need it as String.
public partial class AccountIdentification4Choice {
private object itemField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("IBAN", typeof(string))]
[System.Xml.Serialization.XmlElementAttribute("Othr", typeof(GenericAccountIdentification1))]
public object Item {
get {
return this.itemField;
}
set {
this.itemField = value;
}
}
}
In XML value can be like this:
<CdtrAcct><Id><Othr><Id>BBAN</Id></Othr></Id></CdtrAcct>
or
<CdtrAcct><Id><IBAN>BBAN</IBAN></Id></CdtrAcct>
Update - all resource files:
XML: Priklad_multi.XML
XSD: camt.053.001.02.xsd
Then I parse XML with EPPlus package:
XmlSerializer ser = new XmlSerializer(typeof(Document));
Document resp = ser.Deserialize(new FileStream(file, FileMode.Open)) as Document;
And then I can access XML values like this (for example Stmt-Acct)
resp.BkToCstmrStmt.Stmt[ind].Acct.Id.Item
But I don't know how to access this CdtrAcct section in same way.
Your question is not clear, but as I understand you want to get IBAN tag, right? If so, you could do that easily with Linq To XML. ie:
void Main()
{
string xml1 = "<CdtrAcct><Id><Othr><Id>BBAN</Id></Othr></Id></CdtrAcct>";
string xml2 = "<CdtrAcct><Id><IBAN>BBAN</IBAN></Id></CdtrAcct>";
Console.WriteLine(CheckIban(xml1));
Console.WriteLine(CheckIban(xml2));
}
private string CheckIban(string xml)
{
var iban = XElement.Parse(xml).DescendantsAndSelf("IBAN").FirstOrDefault();
if (iban != null)
{
return (string)iban;
}
iban = XElement.Parse(xml).DescendantsAndSelf("Othr")
.FirstOrDefault();
if (iban != null)
{
return iban.Element("Id").Value;
}
return null;
}
I am generating .cs files from .xsd files using xsd.exe. But when I am adding the files to windows 10 universal blank app, I was getting error as "missing an assembly reference" for System.SerializableAttribute() and System.ComponentModel.DesignerCategoryAttribute("code"). I fixed this by #t.ouvre's trick. Then there were no errors in any of the particular line of the code but when i am building the code i am getting an error saying that " Cannot find type System.ComponentModel.MarshalByValueComponent in module System.dll" and it doesn't specify exactly where the error is. How can I use the file generated by xsd.exe in windows 10 universal app? What are all the things that I need to do with the file to use it for serialization and deserialization (using DataContractSerializer in UWP)
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.81.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class request
{
private usertype userField;
private string versionField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public usertype user
{
get
{
return this.userField;
}
set
{
this.userField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string version
{
get
{
return this.versionField;
}
set
{
this.versionField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.81.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class usertype
{
private string emailField;
private string passwordField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string email
{
get
{
return this.emailField;
}
set
{
this.emailField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string password
{
get
{
return this.passwordField;
}
set
{
this.passwordField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.81.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class NewDataSet
{
private request[] itemsField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("request")]
public request[] Items
{
get
{
return this.itemsField;
}
set
{
this.itemsField = value;
}
}
}
You can fake missing classes (System.SerializableAttribute(),System.ComponentModel.DesignerCategoryAttribute), just add new files with theses classes definitions :
namespace System
{
internal class SerializableAttribute : Attribute
{
}
}
namespace System.ComponentModel
{
internal class DesignerCategoryAttribute : Attribute
{
public DesignerCategoryAttribute(string _) { }
}
}
for serialization and deserialization, you have to use System.Runtime.Serialization.DataContractSerializer. For example :
DataContractSerializer serializer = new DataContractSerializer(typeof(request));
var r = new request { version = "test" };
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, r);
ms.Seek(0, SeekOrigin.Begin);
using (var sr = new StreamReader(ms))
{
string xmlContent = sr.ReadToEnd();
Debug.WriteLine(xmlContent);
ms.Seek(0, SeekOrigin.Begin);
using (XmlReader reader = XmlReader.Create(sr))
{
var deserialized = serializer.ReadObject(reader) as request;
if (deserialized != null && deserialized.version == r.version)
{
Debug.WriteLine("ok");
}
}
}
}
SerializableAttribute()
and DesignerCategoryAttribute
aren't supported in the uwp apps.
To produce a successful class that can be copied directly into the UWP ap , use the following tip :
This is done in a UWP app so u can just go ahead and do this.
Well the XML serialization does have some limitations that is you must have a default constructor without any parameters.
if you are planing to serialize a class that doesn't have a constructor microsoft encourages to use DataContractSerializer for such use cases.
Now the code is quite simple
First instantiate the obj to serialize.
Make a new XmlSerializer object.
Then the XmlWriter obj , since it takes in many different writer classes and u Need to instantiate one i've chosen string builder for demo purposes.
Then just simply call serialize on the serializer obj passing in your obj and the xmlwriter and the xml writer produces the op in the string builder obj.
Then i just turn it into a string.. from here u can do anything with the xml .. save it or play with it ..
the last toUpper method was just added bcoz i needed a line for debug point .. it's not necessary at all ...
private void Button_Click( object sender , RoutedEventArgs e )
{
Animal a = new Animal("Sheep" , 4);
XmlSerializer m = new XmlSerializer(typeof(Animal));
StringBuilder op = new StringBuilder();
var x = XmlWriter.Create(op);
m.Serialize(x , a);
string s = op.ToString();
var p = s.ToUpper();
}
public class Animal
{
public Animal( string name , int legcount )
{
this.name = name;
this.legcount = legcount;
}
public Animal()
{
this.name = "default";
this.legcount = 10000000;
}
public string name { get; set; }
public int legcount { get; set; }
}
The reult of the Serialized class
<?xml version="1.0" encoding="utf-16"?>
<Animal xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>Sheep</name>
<legcount>4</legcount>
</Animal>
UPDATE: By this method you can first copy all of your serialized classes into the app and deserialize them when necessary inside the app.
Now all you need to do is copy your xml into ur new app and implement deserilization using the same techniques i've shown above.
I have XML as shown in sample below -
<?xml version="1.0" encoding="UTF-8"?>
<Model>
<Part Id="0">
<Specs>
<Spec Id = "100" name="SpecNode">
</Spec>
</Specs>
</Part>
</Model>
This is an sample illustration of XML I have , so pls ignore any typing errors.
So in this XML I was able to retrieve Spec as XPathNodeIterator object. So now I would like to go for Part node and get Id attribute value of it. for what I have is XPathNodeIterator object which points to Spec and Part node lies two level above.
So please guide me in code changes I shall do to achieve this requirement.
Her is my code snippet..
System.Xml.XPath.XPathDocument xmlDoc = new System.Xml.XPath.XPathDocument("my.xml");
XPathNavigator xmlNav = xmlDoc.CreateNavigator();
XPathNodeIterator nodes = xmlNav.Select(XPathExpression.Compile(#"//Part/Specs/Spec[#name="SpecNode"]))
Above code sample gives me nodes object. SO my requirement now is to get Id value of Part node which two level above it.
You could use the following LINQ to retrieve the Id-attribute of the Part-node:
const string file = #"D:\Temp\file.xml";
// Load the file.
XDocument doc = XDocument.Load(file);
// Retrieve the attribute selecting the Part-Element and then its Id-attribute.
XAttribute partAttribute = doc.Root.Descendants("Part").Select(element => element.Attribute("Id")).FirstOrDefault();
// Call partAttribute.Value to retrieve its' value.
Console.WriteLine(partAttribute.Value);
Console.Read();
Output: 0.
Do note that if the Part-Element does not exist, a NullReferenceException will be thrown on the partAttribute.Value call. The same happens if the Part-Element exists but the Id does not. Use if(partAttribute != null) to make sure if the value exists.
If you will be getting alot of Part-nodes, you could sort them on ID using KeyValuePairs:
List<KeyValuePair<int, XElement>> partNodesWithIDValue = new List<KeyValuePair<int, XElement>>();
// Get a list of all Part-nodes where an ID exists.
List<XElement> partNodes = doc.Root.Descendants("Part").Where(element => element.Attribute("Id") != null).ToList();
// Sort the XElements according to their ID-value.
foreach (XElement partNode in partNodes)
{
KeyValuePair<int, XElement> elementWithAttribID = new KeyValuePair<int, XElement>(int.Parse(partNode.Attribute("Id").Value), partNode);
partNodesWithIDValue.Add(elementWithAttribID);
}
// Get a list of all Part-elements where the ID = 1.
List<XElement> partNodesWithID1 = partNodesWithIDValue.Where(kvp => kvp.Key == 1).Select(kvp => kvp.Value).ToList();
Seeing as you want to keep your original code, a simple change of XPath does the trick:
XPathNodeIterator nodes = xmlNav.Select(XPathExpression.Compile(#"//Part[#Id]"));
//Part[#Id]: //Part selects all nodes that are named Part. Adding the [#Id] makes it only select those Part-nodes that have an Id-attribute.
Deserialize the XML to an object, it'll be easier to work with and produces cleaner code:
Model class:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Model
{
private ModelPart[] partField;
[System.Xml.Serialization.XmlElementAttribute("Part")]
public ModelPart[] Part
{
get
{
return this.partField;
}
set
{
this.partField = value;
}
}
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class ModelPart
{
private ModelPartSpecs specsField;
private byte idField;
public ModelPartSpecs Specs
{
get
{
return this.specsField;
}
set
{
this.specsField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public byte Id
{
get
{
return this.idField;
}
set
{
this.idField = value;
}
}
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class ModelPartSpecs
{
private ModelPartSpecsSpec specField;
public ModelPartSpecsSpec Spec
{
get
{
return this.specField;
}
set
{
this.specField = value;
}
}
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class ModelPartSpecsSpec
{
private byte idField;
private string nameField;
[System.Xml.Serialization.XmlAttributeAttribute()]
public byte Id
{
get
{
return this.idField;
}
set
{
this.idField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string name
{
get
{
return this.nameField;
}
set
{
this.nameField = value;
}
}
}
Deserialization:
XmlSerializer serializer = new XmlSerializer(typeof(Model));
Model model = null;
using (XmlReader reader = XmlReader.Create("data.xml"))
{
model = (Model)serializer.Deserialize(reader);
}
Now you'll be able to access model as an object and accessing the Part node is as simple as doing:
model.Part
I want to create a serializable C# class that would serialize into:
<metadata>
<entry key="">
<dimensionInfo>
<enabled>false</enabled>
</dimensionInfo>
</entry>
<entry key="">false</entry>
</metadata>
Can you help me? I can't handle different entry node structure:/ Too hard for me:P
Let's begin from the lowest class in the hierarchy:
[Serializable]
public class DimensionInfo
{
[XmlElement("enabled")]
public Boolean Enabled { get; set; }
public DimensionInfo()
{
}
}
As you see, there's nothing special here. Then, proceed with the next:
[Serializable]
public class Entry
{
private DimensionInfo _dimensionInfo = default(DimensionInfo);
private Boolean _containsDimensionInfo = true;
[XmlAttribute("key")]
public String Key { get; set; }
[XmlText(typeof(String))]
public String ContainsDimensionInfo
{
get
{
return CheckDimensionContaining().ToString().ToLower();
}
set
{
_containsDimensionInfo = Boolean.Parse(value);
}
}
[XmlIgnore]
public Boolean ContainsDimensionInfoSpecified
{
get { return !CheckDimensionContaining(); }
}
[XmlElement("dimensionInfo")]
public DimensionInfo DimensionInfo
{
get { return _dimensionInfo; }
set { _dimensionInfo = value; }
}
[XmlIgnore]
public Boolean DimensionInfoSpecified
{
get { return CheckDimensionContaining(); }
}
public Entry()
{
Key = String.Empty;
CheckDimensionContaining();
}
private Boolean CheckDimensionContaining()
{
return _containsDimensionInfo = _dimensionInfo != default(DimensionInfo);
}
}
Here the magic begins. With using selective XmlIgnoreAttribute we can decide the ways the object serializes. The main class only wraps a list of Entries:
[Serializable]
[XmlRoot("metadata")]
public class Metadata
{
[XmlElement("entry")]
public List<Entry> Entries { get; set; }
public Metadata()
{
Entries = new List<Entry>();
}
}
How it is made: entry contains dimensionInfo if it exists, and false if it does not. Try out the code sample in console:
Metadata metadata = new Metadata();
metadata.Entries.Add(new Entry()); //Adding empty entry
//Adding entry with info
metadata.Entries.Add(new Entry() { DimensionInfo = new DimensionInfo() });
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Metadata));
using (FileStream fileStream = new FileStream("info.txt", FileMode.Create))
{
xmlSerializer.Serialize(fileStream, metadata);
}
It gave me the following output:
<metadata>
<entry key="">false</entry>
<entry key="">
<dimensionInfo>
<enabled>false</enabled>
</dimensionInfo>
</entry>
</metadata>
However, this code is not perfect and there's a lot of things to improve, but now you have an idea of how it works
If you ever encounter XML files that you need to consume via .Net there is a tool XML Schema Definition Tool (Xsd.exe) that does the conversion automatically.
Its a command-line tool and to open it you use the Start > All
Programs > Visual Studio 2008/10/12/13 > Visual Studio Tools > Visual
Studio Command Prompt
The syntax to create a class from the XML file is described in the MSDN article. I'll give you an overview of the process to get you started.
Save the xml file to the temp dirtectory:
<metadata>
<entry key="">
<dimensionInfo>
<enabled>false</enabled>
</dimensionInfo>
</entry>
<entry key="">false</entry>
</metadata>
Fire up the Visual Studio Command Prompt
To find out all the options enter xsd /?
Now you need to convert the XML file to a XSD file, this is the command:
xsd "C:\temp\File.xml" /c /outputdir:c:\temp
This will create an XSD file in the temp directory.
Then to convert the XSD file to a Serializable C# class this is the command:
xsd "C:\temp\File.xsd" /c /outputdir:c:\temp
This is the resulting C# Serialized class file:
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class metadata {
private metadataEntry[] itemsField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("entry", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public metadataEntry[] Items {
get {
return this.itemsField;
}
set {
this.itemsField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class metadataEntry {
private metadataEntryDimensionInfo[] dimensionInfoField;
private string keyField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("dimensionInfo", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public metadataEntryDimensionInfo[] dimensionInfo {
get {
return this.dimensionInfoField;
}
set {
this.dimensionInfoField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string key {
get {
return this.keyField;
}
set {
this.keyField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class metadataEntryDimensionInfo {
private string enabledField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string enabled {
get {
return this.enabledField;
}
set {
this.enabledField = value;
}
}
}
Sounds like;
public class Metadata
{
private Dictionary<string, object> entry;
}
Where the dictionary has two item; "false" and another object;
public class dimensionInfo
{
public bool enabled = false;
}
However, you have 2 keys that are empty string. Are you sure the keys shouldn't have a value?
Actually you can create your own custom serialization for any class, so you don't have to stick to the schema when implementing your class.
All you have to do is to create a class which implements the IXmlSerializable interface
You can read more on the topic here:
Custom XML serialization
Otherwise you can use Attributes to control XML serialization.
Using [XmlAttribute] results in serialization as attribute.
Using [XmlText] results in serialization as inner text in the node. (Like the false string inside the other element in your example XML.)
Example:
public class Program
{
static void Main(string[] args)
{
Metadata meta = new Metadata();
meta.entry = new List<Entry>();
var dim = new dimensionInfo();
meta.entry.Add(
new Entry()
{
key = "",
O = dim
}
);
meta.entry.Add(
new Entry()
{
key = "",
text = "false",
O = null
}
);
XmlWriterSettings set = new XmlWriterSettings();
set.NamespaceHandling = NamespaceHandling.OmitDuplicates;
set.OmitXmlDeclaration = true;
set.DoNotEscapeUriAttributes = false;
set.Indent = true;
set.NewLineChars = "\n\r";
set.IndentChars = "\t";
XmlWriter writer = XmlWriter.Create(Console.Out, set);
XmlSerializer ser = new XmlSerializer(typeof(Metadata), "");
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
ser.Serialize(writer, meta, namespaces);
}
[XmlRoot("metadata")]
public class Metadata
{
[XmlElement]
public List<Entry> entry;
}
public class dimensionInfo
{
[XmlElement]
public bool enabled = false;
}
public class Entry
{
[XmlAttribute] // serialized as attribute
public string key = "";
[XmlText] // serialized as text node
public string text = "";
[XmlElement("dimensionInfo")] // serialized as an element
public dimensionInfo O = null;
}
}
This resulted the following XML:
<metadata>
<entry key=""><dimensionInfo><enabled>false</enabled></dimensionInfo></entry>
<entry key="">false</entry>
</metadata>
You can use XSD.exe to autoGenerate C# class in which you can serialize.
A straight forward answer:
Having your class to implement IXmlSerializable interface and using an XmlSerializer to defining the behavior for all the cases you desire.
private DimensionInfo _value;
public void WriteXml(XmlWriter writer)
{
var valueSerializer = new XmlSerializer(typeof (DimensionInfo));
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
writer.WriteStartElement("entry");
writer.WriteAttributeString("key", string.Empty, string.Empty);
// Here you define how you want your XML structure to look like
// E.g. write an empty XML node in case of a null value
if (_value != null)
{
valueSerializer.Serialize(writer, value, ns);
}
writer.WriteEndElement();
}
Would produce this in XML
<entry key="">
<dimensionInfo>
<enabled>true</enabled>
</dimensionInfo>
</entry>
Or in case of a null value
<entry key="" />
A more detailed example with a XmlSerializableDictionary:
I used an XmlSerializableDictionary approach to produce the XML you provided. Again, you can specify precisely how the produced XML should look like in the WriteXml method.
[XmlRoot("metadata")]
public class XmlSerializableDictionary<TValue> : Dictionary<string, TValue>, IXmlSerializable where TValue : class
{
private const string XmlKeyName = "key";
private const string XmlValueName = "entry";
public void WriteXml(XmlWriter writer)
{
var valueSerializer = new XmlSerializer(typeof (TValue));
var ns = new XmlSerializerNamespaces(); ns.Add("", "");
foreach (var key in Keys)
{
writer.WriteStartElement(XmlValueName);
writer.WriteAttributeString(XmlKeyName, string.Empty, key);
var value = this[key];
// Only serialize the value if value is not null, otherwise write the
// empty XML element.
if (value != null)
{
valueSerializer.Serialize(writer, value, ns);
}
writer.WriteEndElement();
}
}
public void ReadXml(XmlReader reader) { /* left out */ }
public XmlSchema GetSchema() { return null; }
}
The DimensionInfo type for reference
[XmlRoot("dimensionInfo")]
public class DimensionInfo
{
[XmlElement("enabled")]
public Boolean Enabled { get; set; }
}
The following code serializes the dictionary to XML
var xmlSerializableDictionary = new XmlSerializableDictionary<DimensionInfo>
{
{"justakey", new DimensionInfo {Enabled = true}},
{"anotherkey", null}
};
var xmlSerializer = new XmlSerializer(typeof (SerializableDictionary<DimensionInfo>));
xmlSerializer.Serialize(File.Open(#"D:\xmlSerializedDictionary.xml", FileMode.Create), serializableDictionary);
Produced XML file:
<?xml version="1.0"?>
<metadata>
<entry key="justakey">
<dimensionInfo>
<enabled>true</enabled>
</dimensionInfo>
</entry>
<entry key="anotherkey" />
</metadata>