LINQ to XML parsing single object - c#

I am trying to parse XML document like this:
<root>
<first>1</first>
<second>2</second>
</root>
To structure like this:
class SomeClass
{
...
public string First;
public string Second;
}
but as far as I understood, I can create new object only in select statement, which only can be applied to collection and root element is not a collection.
Of course, I can select fields separately like:
new SomeClass(doc.Element("first").Value, doc.Element("second").Value);
But I'm really interested if is it possible to do it in one LINQ statement (using doc variable only once and creating object inside the LINQ statement)?
In other words: is it possible to create an object not in Select() method?

The root element may not be a collection, but when you parse the xml, your doc variable is a collection of elements, including root element. So you can still use Select:
string xml = #"<root><first>1</first><second>2</second></root>";
var doc = XDocument.Parse(xml);
var collectionOfSomeClass = doc.Elements()
.Select(x => new SomeClass
{ First = x.Element("first").Value,
Second = x.Element("second").Value
});

Related

Setting model properties from xml element values

I have an xml file that looks like this
<Root>
<Element1>17890</Element>
<Element2>0001</Element2>
<Element3>123451324A</Element3>
<Element4>1</Element4>
<Element5>ABC</Element5>
<Element6>DEF</Element6>
<Element7>99.10</Element7>
<Element8>GHI</Element8>
<Element9>2014-01-25</Element9>
<Element10>JKL</Element10>
<Element11>737268</Element11>
</Root>
And I have a corresponding class that have all the element names as properties. Let's say I have a collection of all the elements such as
IEnumerable<XElement> elements;
How do I set the property values of the class to the element values from the xml file?
The only thing I have thought of is to loop over elements and make a big switch statement with sections such as
...
case "Element3":
model.Element3 = element.Value;
break;
...
Is there a better solution?
Assuming you already have a class Model with the fields you want to get from the xml:
var elements = XDocument.Load("XMLFile1.xml").Root.Elements();
var model = new Model()
{
Element1 = elements.FirstOrDefault(t => t.Name.LocalName == "Element1"),
Element2 = elements.FirstOrDefault(t => t.Name.LocalName == "Element2"),
};
Another option is to create a dictionary, so you don't need to update your model every time a new tag name is added to the xml file.
var model = new Dictionary<string, string>();
foreach(XElement tag in elements)
{
model[tag.Name.LocalName] = tag.Value;
}
Or using Linq
var model = elements.ToDictionary(e => e.Name.LocalName, e => e.Value);
Finally you can generate a dynamic object directly from the xml. There's an example here: Deserialize XML To Object using Dynamic

XElement.Root.Element keeps returning null

I am learning C# and trying to flatten XML following this code from this post
var doc = XDocument.Load("test.xml");
XNamespace ns = "mynamespace";
var member = doc.Root.Element(ns + "member");
// This will *sort* of flatten, but create copies...
var descendants = member.Descendants().ToList();
// So we need to strip child elements from everywhere...
// (but only elements, not text nodes). The ToList() call
// materializes the query, so we're not removing while we're iterating.
foreach (var nested in descendants.Elements().ToList())
{
nested.Remove();
}
member.ReplaceNodes(descendants);
This is my XML (sorry I don't know how to post fancy code style)
<ApplicationExtraction>
<IsCurrent>Yes</IsCurrent>
<ApplicationDate>10/06/2015</ApplicationDate>
<Status>Application Received</Status>
<EquipmentType>Equipment</EquipmentType>
<IsLoan>No</IsLoan>
</ApplicationExtraction>
There is namespace so I changed var member = doc.Root.Element(ns + "member"); to var member = doc.Root.Element("ApplicationExtraction"); but this returns NULL.
I also try XElement sRoot = doc.Root.Element("ApplicationExtraction"); from this post I still get the same result.
I read up Microsoft XElement document but don't see how I can fix this.
What could I have done wrong?
XElement sRoot = doc.Root.Element("ApplicationExtraction");
will look for an element 'ApplicationExtraction' inside the Root.
If you want the Root, just reference
doc.Root
In your XML, doc.Root is the root node i.e. ApplicationExtraction and there is no node ApplicationExtraction inside the root node thus you are getting null.
To fetch any specific node you need(for example):-
XElement member = doc.Root.Element("IsCurrent");
and to fetch the value inside the node:-
string member = (string)doc.Root.Element("IsCurrent");

How can I get elements of a certain name from an XML document as an XML String? (with XDocument)

How can I get elements of a certain name from an XML document as an XML String? (with XDocument)
I.e, say I have this:
<root>
<orange id="orange1"></orange>
<orange id="orange2"></orange>
<orange id="orange3"></orange>
<apple id="apple1"></apple>
<apple id="apple2"></apple>
<apple id="apple3"></apple>
</root>
How can I get only the XML for the apples? Ie the XML string for those three lines?
My current code is:
using (TextReader reader = File.OpenText(xmlFilePath))
{
XDocument xmlDocument = XDocument.Load(reader);
string items = xmlDocument.Descendants("apple").ToString();
}
...but in this example, items ends up as: System.Xml.Linq.XContainer+<GetDescendants>d__a rather than the XML string. I can't seem to find any method which will give me back the XML for the matched elements.
The problem is that you're calling ToString() on the result of calling Descendants(). It's not really clear what you expected that to do, but you are getting the elements correctly. For example:
using (TextReader reader = File.OpenText(xmlFilePath))
{
// Any reason for not using XDocument.Load(xmlFilePath)?
XDocument xmlDocument = XDocument.Load(reader);
var items = xmlDocument.Descendants("apple");
foreach (var item in items)
{
Console.WriteLine(item.Attribute("id").Value); // Or whatever
}
}
If you want to concatenate the results of converting each XElement to string, you could use:
var items = string.Join("", xmlDocument.Descendants("apple"));
or
var items = string.Concat(xmlDocument.Descendants("apple"));
Use String.Concat(xmlDocument.Descendants("apple")).
You are using ToString() on a collection of xml elements, hence your results. If I'm reading your requirements correctly, you need something like:
var items = String.Join(Environment.NewLine,
xmlDocument.Descendants("apple")
.Select(e => e.ToString()));

Query Collection of all XML Elements

I'm looking to parse an XML file, and have the ability to search any element.
My XML code looks rather nontraditional (out of my control):
<DiagReport>
<LicensingData>
<ToolVersion>6.3.9431.0</ToolVersion>
<LicensingStatus>SL_LICENSING</LicensingStatus>
</LicensingData>
<Health>
<Result>PASS</Result>
<TamperedItems></TamperedItems>
</Health>
<Genuine>
<ServerProps>GenuineId</ServerProps>
</Genuine>
</DiagReport>
I'd like to load each singular element into a collection, i.e. one collection including ToolVersion, Result, etc. From there, I'd like to iterate through each element/name pair, and depending upon the element, do something different:
if (element.Equals(Result))
//do something with "PASS"
Is this possible to do with LINQ?
You can use LINQ to XML to iterate through all second level elements:
var xdoc = XDocument.Load(#"C:\test.xml");
foreach (var element in xdoc.Element("DiagReport").Elements().Elements())
{
if (element.Name == "Result")
{
string value = element.Value;
}
}
You can use Linq to Xml to query your Xml document, you can search Elements, Attributes and grab what you need.
More information can be found here: Basic Queries (LINQ to XML)
And you can creat simple querys like below.
XElement root = XElement.Parse(File.ReadAllText("C:\\Test.xml"));
var test = new
{
ToolVersion = root.Descendants("ToolVersion").FirstOrDefault().Value,
Result = root.Descendants("Result").FirstOrDefault().Value
};

C# - Linq to XML - Exclude elements from query

I have this XML file:
<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
<MyXml>
All 3 elements that are called 'MandatoryElementX' will always appear in the file. The elements called 'CustomElementX' are unknown. These can be added or removed freely by a user and have any name.
What I need is to fetch all the elements that are not MandatoryElements. So for the file above I would want this result:
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
I don't know what the names of the custom elements may be, only the names of the 3 MandatoryElements, so the query needs to somehow exclude these 3.
Edit:
Even though this was answered, I want to clarify the question. Here is an actual file:
<Partner>
<!--Mandatory elements-->
<Name>ALU FAT</Name>
<InterfaceName>Account Lookup</InterfaceName>
<RequestFolder>C:\Documents and Settings\user1\Desktop\Requests\ALURequests</RequestFolder>
<ResponseFolder>C:\Documents and Settings\user1\Desktop\Responses</ResponseFolder>
<ArchiveMessages>Yes</ArchiveMessages>
<ArchiveFolder>C:\Documents and Settings\user1\Desktop\Archive</ArchiveFolder>
<Priority>1</Priority>
<!--Custom elements - these can be anything-->
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
</Partner>
The result here would be:
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
You can define a list of mandatory names and use LINQ to XML to filter:
var mandatoryElements = new List<string>() {
"MandatoryElement1",
"MandatoryElement2",
"MandatoryElement3"
};
var result = xDoc.Root.Descendants()
.Where(x => !mandatoryElements.Contains(x.Name.LocalName));
Do you have created this xml or do you get it by another person/application?
If it's yours I would advise you not to number it. You can do something like
<MyXml>
<MandatoryElement id="1">value<\MandatoryElement>
<MandatoryElement id="2">value<\MandatoryElement>
<MandatoryElement id="3">value<\MandatoryElement>
<CustomElement id="1">value<\CustomElement>
<CustomElement id="2">value<\CustomElement>
<MyXml>
In the LINQ-Statement you don't need the List then.
Your question shows improperly formatted XML but I am assuming that is a typo and the real Xml can be loaded into the XDocument class.
Try this...
string xml = #"<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
</MyXml> ";
System.Xml.Linq.XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Root.Descendants()
.Where(x => !x.Name.LocalName.StartsWith("MandatoryElement"));
lets say TestXMLFile.xml will contain your xml,
XElement doc2 = XElement.Load(Server.MapPath("TestXMLFile.xml"));
List<XElement> _list = doc2.Elements().ToList();
List<XElement> _list2 = new List<XElement>();
foreach (XElement x in _list)
{
if (!x.Name.LocalName.StartsWith("Mandatory"))
{
_list2.Add(x);
}
}
foreach (XElement y in _list2)
{
_list.Remove(y);
}

Categories