I've got an xml-file that looks like this for example:
<?xml version="1.0" encoding="UTF-8"?>
<Adresses>
<Message>
<Header>
<MessageID>96</MessageID>
<Timestamp>22.08.2014 10:25:01</Timestamp>
</Header>
<Body>
<Person SurName="Muster" Prename="Max">
<Adress Street="Street 1"/>
</Person>
<Person SurName="Muster" Prename="Max">
<Adress Street="Street 1"/>
</Person>
<Person SurName="Muster" Prename="Max">
<Adress Street="Street 1"/>
</Person>
</Body>
</Message>
</Adresses>
From this xml I only want the part inside the body-tags. I do the deserialization with the XmlSerializer and annotaions. So I have models that look like this
[XmlRoot("Body")]
public class BodyXml
{
public BodyXml()
{}
[XmlElement("Person")]
public Person[] Persons { get; set; }
}
Now my question is how can I get the XmlSerializer to serialize from the body-tag and not from the adresses-tag? Do I need another annotation somewhere in my models?
thanks and greets
Depending on other constraints, either consider writing a quick and dirty wrapper that would deserialize the whole XML (with BodyXml as it's member), or alternatively select only the relevant part of your xml, e.g.:
var serializer = new XmlSerializer(typeof(BodyXml));
var xDoc = XDocument.Parse(YOUR_XML_STRING);
using (var xmlReader = xDoc.Descendants("Body").Single().CreateReader())
{
var result = serializer.Deserialize(xmlReader);
}
EDIT: without any context I'd go with the latter.
use XmlIgnoreAttribute
You can Add Address property as below
[XmlIgnoreAttribute]
public AddressClass Adress{get;set;}
Here AddressClass may type of your Address property or some other class.
Related
I have to generate (serialize to) XML from object with array of (Order) elements.
Order class generated from XSD has sequence attribute:
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "token")]
public string Sequence;
I am using .Net XMLSerializer, but it does not generate automagically for every Order element Sequence attribute.
Having:
Order[] Orders = new Order[2] {...}
I have to get:
<Order Sequence="1">..</Order>
<Order Sequence="2">..</Order>
And for just one, single element it should render:
<Order Sequence="1">..</Order>
Does anyone know how to make XMLSerialzier renders this attribute automagically? Or do I need to manually set Sequence for every Order element?
Cheers
AFAIK there is no way to achieve this with out-of-the-box methods. This leaves you with the following options:
IXmlSerializable
Implement IXmlSerializable on the object that contains the array of Orders. This way, you can either serialize the Orders manually or set the sequence number of the Order before serializing the object into the XmlWriter using the XmlSerializer.Serialize method:
public class OrdersContainer : IXmlSerializable
{
public Order[] Orders;
public void WriteXml(XmlWriter writer)
{
// Serialize other properties
writer.WriteStartElement("Orders");
var ser = new XmlSerializer(typeof(Order));
for(var i = 0; i < Orders.Length; i++)
{
Orders[i].Sequence = (i + 1).ToString();
ser.Serialize(writer, Orders[i]);
}
writer.WriteEndElement(); // Orders
}
// ...
}
This generated the following XML:
<?xml version="1.0" encoding="utf-16"?>
<OrdersContainer>
<Orders>
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Sequence="1">
<Name>Order 1</Name>
</Order>
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Sequence="2">
<Name>Order 2</Name>
</Order>
</Orders>
</OrdersContainer>
The XmlSerializer places some namespace declarations on the Order elements, but that doesn't hurt. My sample class had a Name property on the Order class.
The downside of this approach is that you have to implement the serialization of the OrdersContainer class manually, including the deserialization.
Linq to XML
The second option is to use Linq to XML for the serialization part. You'd also have to implement the serialization manually, but you could use the otb XmlSerializer for deserialization (on the other hand you might want to avoid to mix two different frameworks). You could serialize the OrdersContainer class as follows and also write the sequence number:
var doc = new XDocument(new XElement("OrdersContainer",
new XElement("Orders",
cont.Orders.Select((x, i) => new XElement("Order",
new XAttribute("Sequence", (i + 1).ToString()),
new XElement("Name", x.Name))))));
doc.Save(writer);
This created the following XML:
<?xml version="1.0" encoding="utf-16"?>
<OrdersContainer>
<Orders>
<Order Sequence="1">
<Name>Order 1</Name>
</Order>
<Order Sequence="2">
<Name>Order 2</Name>
</Order>
</Orders>
</OrdersContainer>
Please note that the sample uses an overload of the Select method that also receives the index of the item so that the sequence number can be created based upon the position in the array.
If order is a class of yours, you can add a property with [XmlAttributeAttribute]
class Order {
[XmlAttribute()]
public int Sequence { get; set; }
But you should set this Sequence property in all items of your list.
this works fine -
var o = new XmlSerializerNamespaces();
o.Add("", "");
var ser = new XmlSerializer(typeof(List<Order>), "");
using (var sw = new StringWriter())
{
ser.Serialize(sw, new List<Order> {
new Order { sequence = "1", MyProperty = 1 },
new Order { sequence = "2", MyProperty = 2 } },
o);
var t = sw.ToString();
}
AND
[XmlAttribute(AttributeName = "sequence", DataType = "string")]
public string sequence { get; set; }
I have a XML file, organized in a way similar to this:
<People>
<Person>
<Name>John</Name>
<SurName>Smith</SurName>
</Person>
<Person>
<Name>Jack</Name>
<SurName>Woodman</SurName>
</Person>
[...]
</People>
<Professions>
<Person>
<SurName>Smith</SurName>
<Profession>Blacksmith</Profession>
</Person>
<Person>
<SurName>Woodman</SurName>
<Profession>Lumberjack</Profession>
</Person>
[...]
</Professions>
Ok, here we go.
I am parsing this using Xml.Linq and I've got sth like this code:
foreach (XElement datatype in RepDoc.Element("People").Descendants("Persons"))
{
Name=datatype.Element("Name").Value;
SurName=datatype.Element("SurName").Value;
}
But, the question is if it is possible to open another foreach, inside another method, starting on the current datatype position without parsing from the beginning again.
As if the method inherit the XElement datatype, so I could start parsing from the same <Person>node.
It sounds stupid, but I can't put the original .xml because it is huge and has millions of information that would just confuse us more.
Thanks :)
Okay, so I believe you're looking for yield. This would often be used like this:
public IEnumerable<Person> GetPeople()
{
foreach (XElement datatype in RepDoc.Element("People").Descendants("Persons"))
{
var p = new Person
{
Name = datatype.Element("Name").Value,
SurName = datatype.Element("SurName").Value
};
yield return p;
}
}
and then you could use that method like this:
foreach (Person p in MyClass.GetPeople())
The benefit here is that you only read as much of the XML file as is necessary because if you break out of the outer-loop, everything stops.
i have an xml document of this kind
<xml>
<person name="a">
<age>21</age>
<salary>50000></salary>
</person>
<person name="b">
<age>25</age>
<salary>30000></salary>
</person>
<person name="c">
<age>30</age>
<salary>60000></salary>
</person>
<person name="d">
<age>35</age>
<salary>150000></salary>
</person>
</xml>
Now im trying to validate this document by passing this to a method like this
validate(string file)
{
// here i have some logic
// say i am trying to check if the salary is >50000 and age > 30
// if it doesn't satisfy the condition i have to return an error
}
I am able to achieve this . What i really want is to know where exactly the error in xml document is , like which line and column.
How should i do this ? any suggestions?
You are closing your 'age' tag like this
<age>35<age/>
It should be like
<age>35</age>
hope this works for you.
Firstly, you have not specified how you are parsing your XML in c#. It matters.
Now as for validity of your XML document, a valid xml document should have these:
XML documents must have a root element
XML elements must have a closing tag
XML tags are case sensitive
XML elements must be properly nested
-XML attribute values must be quoted
now try adding this to the top of your xml doc
<?xml version="1.0" encoding="ISO-8859-1"?>
Now, to parse XML document, you can use either XmlDocument class or Linq's XDocument class.
Lets take example of XmlDocument.
if you have a xml string, load Xml as below:
XmlDocument doc = new XmlDocument();
doc.LoadXml(stringXML);
foreach(XmlNode node in doc.SelectNodes("xml/person/salary"))
{
var strSalary = node.InnerText;
var intSalary = Convert.ToInt32(strSalary??0);
}
if you have a xml file, load Xml as below:
XmlDocument doc = new XmlDocument();
doc.Load(XMLFilePath);
Full example:
string xml = #"<xml>
<person name=""a"">
<age>21</age>
<salary>50000</salary>
</person>
<person name=""b"">
<age>25</age>
<salary>30000</salary>
</person>
<person name=""c"">
<age>30</age>
<salary>60000</salary>
</person>
<person name=""d"">
<age>35</age>
<salary>150000</salary>
</person>
</xml>";
using (var sr = new StringReader(xml))
{
var xml2 = XDocument.Load(sr, LoadOptions.SetLineInfo);
foreach (var person in xml2.Root.Elements())
{
//string name = (string)person.Attribute("name"); // Unused
int age = (int)person.Element("age");
int salary = (int)person.Element("salary");
// Your check
bool error = salary > 50000 && age > 30;
if (error)
{
// IMPORTANT PART HERE!!!
int lineNumber = -1;
int colNumber = -1;
var lineInfo = (IXmlLineInfo)person;
if (lineInfo.HasLineInfo())
{
lineNumber = lineInfo.LineNumber;
colNumber = lineInfo.LinePosition;
}
return string.Format("Error on line {0}, col {1}", lineNumber, colNumber);
// END IMPORTANT PART!!!
}
}
}
The "trick" is that XElement implement IXmlLineInfo (as explained here), but you have to load the document with LoadOptions.SetLineInfo.
I am playing around with this simple tutorial in C# and here is the kind of XML you can GET.
<Person xmlns="http://schemas.datacontract.org/2004/07/RESTfulDemo"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Age>23</Age>
<ID>1</ID>
<Name>Bob Kohler</Name>
</Person>
Here is the Person.cs class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
namespace RESTfulDemo
{
[DataContract]
public class Person
{
[DataMember]
public string ID;
[DataMember]
public string Name;
[DataMember]
public string Age;
}
}
1) How should can I add attributes / Prefixes in my XML for each data member ?
2) How can I set the heading of my XML to this (or anything else) :
<?xml version="1.0"?>
question 2) can be done when initializing a document:
XDocument document = new XDocument(new XDeclaration("1.0", "utf-16", "yes"));
as for question 1), from what I have understood, if you have such XML file:
<CATALOG>
<CD>
<TITLE> ... </TITLE>
<ARTIST> ... </ARTIST>
<YEAR> ... </YEAR>
</CD>
</CATALOG>
and you need to add an attribute "id" for the CD node,
(where the id is automatically incremented)
XmlNodeList list = document.GetElementsByTagName("CATALOG");
int i = 0;
foreach (XmlNode CD in list)
{
i++;
XmlAttribute idAttr = document.CreateAttribute("id");
idAttr.Value = i.ToString();
CD.Attributes.Append(idAttr); //to append the created attribute and its value to the CD node
}
So, here is what I did to overcome this problem. The solution does not involve Serialization but at least it enables to format the response like you want.
(1) Put XElement from System.xml.linq as return type of each method and build the xml with the XElement class in each method.
(2) Use exactly the code provided here to add the xml declaration on top of your xml response. Thanks to #Dash for the link.
My XmlSerializer code is outputting Xml that is much more verbose than I require, how can I control the output settings properly? This is the code:
var stream = new MemoryStream();
var xmlSerializer = new XmlSerializer(objectToSerialize.GetType());
xmlSerializer.Serialize(stream, objectToSerialize);
string xml = encoding != null ? encoding.GetString(stream.ToArray())
: Encoding.Default.GetString(stream.ToArray());
And the output looks like this:
<?xml version="1.0"?>
<ArrayOfProduct xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Product>
<Id>1</Id>
<ProductCode>A</ProductCode>
<ProductDescription>Product A</ProductDescription>
<Obsolete xsi:nil="true"></Obsolete>
</Product>
</ArrayOfProduct>
I want to be able to format the Xml like so:
Remove the xsi:nil="true" from lines with no value
Collapse empty tags like <Obsolete></Obsolete> into the simple form <Obsolete />
When you decorate the class and members using [Serializable] the objects are serialized using the old methods which does not contains xsi:nil="true
But when you decorate the class with [DataContract] attribute the XmlSerializer will include the xsi:nil="true.
One way to remove xsi:nil="true is to
decorate the class with [Serializable] attribute.