I would like to directly serialize an attribute of an Subelement with an occurence of 0..1 into a property, like so:
public class Root {
// Which attributes are needed here?
public TestEnum TestEnum { get; set; }
}
public enum TestEnum {
[XmlEnum("tst1")]
Test1,
[XmlEnum("tst2")]
Test2
}
The XML Structure simplified:
<root>
<Element testType="tst1"/>
</root>
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = (Root)serializer.Deserialize(reader);
}
}
[XmlRoot("root")]
public class Root
{
// Which attributes are needed here?
private TestEnum TestEnum { get; set; }
public Element Element
{
get { Element e = new Element(); e.testType = TestEnum.ToString(); return e; }
set { TestEnum = (TestEnum)Enum.Parse(typeof(TestEnum), value.testType); }
}
}
public class Element
{
[XmlAttribute]
public string testType { get; set; }
}
public enum TestEnum
{
tst1,
tst2
}
}
Related
I am using the standard .NET XmlSerializer to deserialize the following xml:
<root>
<Element>
<Grouping1>
<Item1>First</Item1>
<Item2>Second</Item2>
</Grouping1>
<Grouping2>
<Item3>Third</Item3>
</Grouping2>
</Element>
</root>
I would like to serialize it into the following class:
class Element
{
[XmlElement("Item1")]
public string Item1 { get; set; }
[XmlElement("Item2")]
public string Item2 { get; set; }
[XmlElement("Item3")]
public string Item3 { get; set; }
}
Which of course doesn't work, because - for instance - <Item1> isn't located in <Element> but in the logical container <Grouping1>.
The question:
Is there a way of telling the XmlSerializer to look for the Item1 in the <Grouping1> element?
Something downs the lines of [XmlElement("Grouping1.Item1")]
PS: I don't want to create a Grouping1 class (as suggested here) because the groupings are only logical containers and shouldn't have their own class.
Using Xml Linq. A custom serializer would be much more complicated
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication4
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Element> elements = doc.Descendants("Element").Select(x => new Element()
{
Item1 = (string)x.Descendants("Item1").FirstOrDefault(),
Item2 = (string)x.Descendants("Item2").FirstOrDefault(),
Item3 = (string)x.Descendants("Item3").FirstOrDefault()
}).ToList();
}
}
class Element
{
public string Item1 { get; set; }
public string Item2 { get; set; }
public string Item3 { get; set; }
}
}
Here is what serializer would look like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml.Schema;
namespace ConsoleApplication4
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = (Root)serializer.Deserialize(reader);
}
}
[XmlRoot("root")]
public class Root
{
[XmlElement("Element")]
public List<Element> Element { get; set; }
}
public class Element : IXmlSerializable
{
private string Item1 { get; set; }
private string Item2 { get; set; }
private string Item3 { get; set; }
public void WriteXml(XmlWriter writer)
{
XElement element = new XElement("Element", new object[] {
new XElement("Grouping1", new object[] {
new XElement("Item1", Item1),
new XElement("Item2", Item2)
}),
new XElement("Grouping2", new XElement("Item3", Item3))
});
element.WriteTo(writer);
}
public void ReadXml(XmlReader reader)
{
XElement element = (XElement)XElement.ReadFrom(reader);
Item1 = (string)element.Descendants("Item1").FirstOrDefault();
Item2 = (string)element.Descendants("Item2").FirstOrDefault();
Item3 = (string)element.Descendants("Item3").FirstOrDefault();
}
public XmlSchema GetSchema()
{
return (null);
}
}
}
I don't want to create a Grouping1 class...
Not sure whether that's possible using serialization without creating the objects that output this XML.
An alternative to serialization is using the XmlReader to extract the properties in question (Item1, Item2, and Item3) and create a list of Element type, and XmlWriter to generate the whole XML file. Both classes provide fast, non-cached, and forward-only way to read and write XML files.
Assuming your XML file has multiple Element entries like:
<root>
<Element>
<Grouping1>
<Item1>First1</Item1>
<Item2>Second1</Item2>
</Grouping1>
<Grouping2>
<Item3>Third1</Item3>
</Grouping2>
</Element>
<Element>
<Grouping1>
<Item1>First2</Item1>
<Item2>Second2</Item2>
</Grouping1>
<Grouping2>
<Item3>Third2</Item3>
</Grouping2>
</Element>
</root>
... and a class named Element:
//The serializable attribute is not required here...
public class Element
{
public Element() { }
public string Item1 { get; set; }
public string Item2 { get; set; }
public string Item3 { get; set; }
public override string ToString() => $"{Item1}, {Item2}, {Item3}";
}
Create a function to read the file, create and return a list of Element items:
public List<Element> ReadElements(string xmlFile)
{
var elements = new List<Element>();
Element ele = null;
using (var xr = XmlReader.Create(xmlFile))
while (xr.Read())
{
if (xr.NodeType == XmlNodeType.Element)
{
if (xr.Name == "Element")
ele = new Element();
else if (xr.Name == "Item1")
{
xr.Read();
ele.Item1 = xr.Value;
}
else if (xr.Name == "Item2")
{
xr.Read();
ele.Item2 = xr.Value;
}
else if (xr.Name == "Item3")
{
xr.Read();
ele.Item3 = xr.Value;
}
}
else if (xr.NodeType == XmlNodeType.EndElement)
if (xr.Name == "Element")
elements.Add(ele);
}
return elements;
}
... and a method to write:
public void WriteElements(string xmlFile, IEnumerable<Element> elements)
{
var xmlSet = new XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = true,
WriteEndDocumentOnClose = true,
};
using (var xr = XmlWriter.Create(xmlFile, xmlSet))
{
xr.WriteStartElement("root");
foreach(var ele in elements)
{
xr.WriteStartElement("Element");
xr.WriteStartElement("Grouping1");
xr.WriteStartElement("Item1");
xr.WriteString(ele.Item1);
xr.WriteEndElement();
xr.WriteStartElement("Item2");
xr.WriteString(ele.Item2);
xr.WriteEndElement();
xr.WriteEndElement();
xr.WriteStartElement("Grouping2");
xr.WriteStartElement("Item3");
xr.WriteString(ele.Item3);
xr.WriteEndElement();
xr.WriteEndElement();
xr.WriteEndElement();
}
}
}
A test to read and write the file like:
private void TheCaller()
{
var xmlFile = "XmlFile.xml";
var elements = ReadElements(xmlFile);
elements.ForEach(x => Console.WriteLine(x));
//...
WriteElements(xmlFile, elements);
}
Prints in my output window:
First1, Second1, Third1
First2, Second2, Third2
This is how my Xml should look after XML Serialization:
<value xsi:type="CD" otherAttributes= "IDK">
.
.
.
</value>
Thats my C# code to it:
public class Valué
{
[XmlAttribute(AttributeName ="xsi:type")]
public string Type { get; set; } = "CD";
[XmlAttribute(attributeName: "otherAttributes")]
public string OtherAttributes { get; set; } = "IDK"
}
Apparently the XmlSerializer can't serialize colons (:) in attributenames.... how do i fix this problem?
If i remove the colon from the attributeName itm works out fine ..
Do it the correct way :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Valué value = new CD() { OtherAttributes = "IDK" };
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(Valué));
serializer.Serialize(writer, value);
}
}
[XmlInclude(typeof(CD))]
public class Valué
{
}
public class CD : Valué
{
[XmlAttribute(attributeName: "otherAttributes")]
public string OtherAttributes { get; set; }
}
}
I have the following simple XML file and I am tying to read the attribute code, using a C# .NET Core Console. I really appetite for any help.
<course type="IT" date="19.09.2019">
<line code="IT001"/>
</course>
UPDATE
I need the result as an object.
Use xml serialization to get a class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Course));
Course course = (Course)serializer.Deserialize(reader);
}
}
[XmlRoot("course")]
public class Course
{
[XmlAttribute("type")]
public string _type { get; set; }
public DateTime _date { get; set; }
[XmlAttribute("date")]
public string date {
get { return _date.ToString("dd.MM.yyyy"); }
set { _date = DateTime.ParseExact(value, "dd.MM.yyyy", System.Globalization.CultureInfo.InvariantCulture); }
}
private string _code { get; set; }
[XmlElement("line")]
public Line line
{
get { return new Line() { code = _code }; }
set { _code = value.code; }
}
}
[XmlRoot("line")]
public class Line
{
[XmlAttribute("code")]
public string code { get; set; }
}
}
Many ways to skin this cat. Here is a solution by making use of XPath
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml("<course type=\"IT\" date=\"19.09.2019\"> <line code=\"IT001\"/></course>");
var attrVal = xmlDoc.SelectSingleNode("/course/line/#code").Value;
Console.WriteLine("Attribute 'code' value is " + attrVal);
I have the following XML file:
<?xml version="1.0" encoding="UTF-8"?>
<TestConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Barcode>MB-B3-00</Barcode>
<TestSuites>
<Test>USB A Slave Port</Test>
<Test>USB B Host Port</Test>
</TestSuites>
</TestConfiguration>
I want to deserialize it into the following class:
public class TestConfiguration
{
private string _barcode;
private string[] _testSuites;
private string[] _testcase;
//Product barcode
public string Barcode
{
get{return this._barcode;}
set{this._barcode = value;}
}
//Test suites
[System.Xml.Serialization.XmlArrayItemAttribute("Test", IsNullable = false)]
public string[] Testsuites
{
get{return this._testSuites;}
set{this._testSuites = value;}
}
//individual test
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Testcase
{
get{return this._testcase;}
set{this._testcase = value;}
}
}
My deserialization code is:
XmlSerializer serializer = new XmlSerializer(typeof(TestConfiguration));
StreamReader reader = new StreamReader(filename);
TestConfiguration _testConfig = (TestConfiguration)serializer.Deserialize(reader);
reader.Close();
However, _testConfig object only contains Barcode value and the properties Testcase and TestSuites are null. Any advice please?
You are very close. The name of your property, Testsuites, doesn't quite match the name of the element <TestSuites> - the capitalization of the letter S differs, and XML Tags are Case Sensitive.
To fix, rename the property or attach an XmlArrayAttribute with the correct element name:
//Test suites
[System.Xml.Serialization.XmlArray("TestSuites")]
[System.Xml.Serialization.XmlArrayItem("Test", IsNullable = false)]
public string[] Testsuites
{
get { return this._testSuites; }
set { this._testSuites = value; }
}
Try this. You can eliminate the tag and have an array of just element. I can show you how.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(TestConfiguration));
StreamReader reader = new StreamReader(FILENAME);
TestConfiguration _testConfig = (TestConfiguration)serializer.Deserialize(reader);
reader.Close();
}
}
[XmlRoot("TestConfiguration")]
public class TestConfiguration
{
private string _barcode;
private string[] _testSuites;
private string[] _testcase;
//Product barcode
[XmlElement("Barcode")]
public string Barcode { get; set; }
//Test suites
[XmlElement("TestSuites")]
public TestSuites testSuites { get; set; }
}
//individual test
[XmlRoot("TestSuites")]
public class TestSuites
{
[XmlElement("Test")]
public List<string> test {get;set;}
}
}
I have a string like this
<InitParams>
<myparams>
<Ad>true</Ad>
<Ay>true</Ay>
<Sd>false</Sd>
</myparams>
<myContent>
<Item>
<IM>true</IM>
<AL>1234</AL>
</Item>
</myContent>
</InitParams>
I need the value between the tags <IM> and <AL>. Being new to C# and .net not sure what would be the best way to do it. Read up on xmlDoc and linq but sounds like overkill for this small need.
The whole point of something like LINQ to XML was to prevent overkill, because it's so easy to use:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace WhateverNamespaceYouWant
{
public class Item
{
public bool IM { get; set; }
public int AL { get; set; }
}
public class ItemsRepository
{
public IEnumerable<Item> GetAllItemsInXML()
{
var _items = new List<Item>();
var doc = XDocument.Load("this");
// finds every node of Item
doc.Descendants("Item").ToList()
.ForEach(item =>
{
var myItem = new Item() // your domain type
{
IM = item.Element("IM").Value.ConvertToValueType<bool>(),
AL = item.Element("AL").Value.ConvertToValueType<int>(),
};
_items.Add(myItem);
});
return _items;
}
}
public static class Extensions
{
public static T ConvertToValueType<T>(this string str) where T : struct
{
try
{
return (T)Convert.ChangeType(str, typeof(T));
}
catch
{
return default(T);
}
}
}
}