How to use LINQ to XML to retrieve nested arrays? - c#

Here's a sample of the XML I want to read:
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<result>
<properties type="array">
<property>
<registers type="array">
<register>
<dials type="integer">6</dials>
</register>
<register>
<dials type="integer">6</dials>
</register>
</registers>
<unit-balance type="integer">-104</unit-balance>
</property>
</properties>
<account-number>9001234</account-number>
</result>
<version>1.0</version>
</hash>
I can read the first level with the following code, but how to get the registers, and have them associated with the corresponding property?
var rawProperties = from property in customerXml.Descendants("property")
select new
{
UnitBalance = property.Element("unit-balance").Value
};

Something like this:
var rawProperties = customerXml.Descendants("property")
.Select(arg =>
new
{
UnitBalance = arg.Element("unit-balance").Value,
Registers = arg.Descendants("dials").Select(x => x.Value).ToList()
})
.ToList();

Related

Dynamically Creating LINQ Query to query XML Elements

I have an XML similar to below,
<?xml version="1.0" encoding="utf-8"?>
<body>
<Result>
<Students>
<Student>
<Firstname Value="Max" />
<LastName Value="Dean" />
</Student>
<Student>
<Firstname Value="Driz" />
<LastName Value="Jay" />
</Student>
</Students>
</Result>
</body>
I'm trying to figure out if its possible to write some code to dynamically build a Linq query from something like,
string pathToElements = "Result.Students.Student";
var individualItems = pathToElements.Split(".");
to something like,
XElement document = XElement.Parse(xml);
var evaluatedResult = document.Elements("Result")
.Elements("Students")
.Elements("Student")
.ToList();
basically the individualItems has 3 items "Result", "Students" and "Student". I'm exploring possible ways to create a dynamic query to accomplish something like,
document.Elements("Result")
.Elements("Students")
.Elements("Student")
.ToList();
For n number of Items I would just need to keep appending .Elements("") to the query.
Any advice on how to achieve this, my first instinct was to use a string builder and have the query as a string. I was able to do this but was not able to find out how a LINQ query which is in the form of a string can be executed.
Thanks in advance
You can query XML using XPath (it's a string so you can create it dynamically) then you can deserialize the item
https://learn.microsoft.com/en-us/troubleshoot/developer/dotnet/framework/general/query-xpathdocument-xpath-csharp
The output of this code
using System;
using System.Xml.XPath;
using System.IO;
public class Program
{
public static void Main()
{
var xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<body>
<Result>
<Students>
<Student>
<Firstname Value=""Max"" />
<LastName Value=""Dean"" />
</Student>
<Student>
<Firstname Value=""Driz"" />
<LastName Value=""Jay"" />
</Student>
</Students>
</Result>
</body>";
using (TextReader tr = new StringReader(xml))
{
var xdoc = new XPathDocument(tr);
var nav = xdoc.CreateNavigator();
var nodeIter = nav.Select("/body/Result/Students/Student");
while (nodeIter.MoveNext())
{
Console.WriteLine("Student: {0}", nodeIter.Current.InnerXml);
};
}
}
}
is
Student: <Firstname Value="Max" />
<LastName Value="Dean" />
Student: <Firstname Value="Driz" />
<LastName Value="Jay" />

How to insert a child tag using XDocument and XElement?

I have this xml schema which I need to add a child tag to:
<?xml version="1.0" encoding="utf-8"?>
<PFA>
<Entity id="123" action="add" date="04-Nov-2017"></Entity>
<Entity id="125" action="add" date="05-Nov-2017"></Entity>
</PFA>
and the expected result is:
<?xml version="1.0" encoding="utf-8"?>
<PFA>
<Record>
<Entity id="123" action="add" date="04-Nov-2017"></Entity>
<Entity id="125" action="add" date="05-Nov-2017"></Entity>
</Record>
</PFA>
I have tried with this code:
var doc = XDocument.Load("data.xml");
var record = new XElement("Record");
doc.Element("PFA").Add(record);
doc.Save("data.xml");
But my result is below. It has the closing tag, but it's missing the opening tag.
<?xml version="1.0" encoding="utf-8"?>
<PFA>
<Entity id="123" action="add" date="04-Nov-2017">
</Entity>
</Record>
</PFA>
Updated:
I tried this solution with Elements:
var doc = XDocument.Load(file.FullName);
var record = new XElement("Record");
var pfa = doc.Element("PFA");
var entities = pfa.Elements("Entity");
entities.Remove(); // remove <Entity> from <PFA>
pfa.Add(record); // add <Record> to <PFA>
record.Add(entities); // add <Entity> to <Record>
doc.Save(file.FullName);
But still the opening record tag is missing,
and the output becomes:
<?xml version="1.0" encoding="utf-8"?>
<PFA>
<Record />
</PFA>
If I use Element:
var doc = XDocument.Load(file.FullName);
var record = new XElement("Record");
var pfa = doc.Element("PFA");
var entities = pfa.Element("Entity");
entities.Remove(); // remove <Entity> from <PFA>
pfa.Add(record); // add <Record> to <PFA>
record.Add(entities); // add <Entity> to <Record>
doc.Save(file.FullName);
The output becomes:
<?xml version="1.0" encoding="utf-8"?>
<PFA>
<Entity id="123" action="add" date="04-Nov-2017"></Entity></Record>
<Record><Entity id="123" action="add" date="04-Nov-2017"></Entity></Record>
<Record><Entity id="125" action="add" date="05-Nov-2017"></Entity></Record>
</PFA>
The solution is actually quite easy. You remove the Entity tags from the PFA tag, then add the new Record tag to the PFA tag and readd the Entity tags to the new added Record tag. The code might look like this:
var pfa = doc.Element("PFA");
IList<XElement> entities = pfa.Elements("Entity").ToList();
foreach (var entity in entities)
{
entity.Remove(); // Remove all the <Entity> tags from <PFA>
}
var record = new XElement("Record");
pfa.Add(record); // Add the new <Record>
foreach (var entity in entities)
{
record.Add(entity); // Add the <Entity> tags back to <Record>
}

Retrieve a value from an XML

Here is my xml file:
<?xml version="1.0" encoding="utf-8"?>
<PutUserLinkRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<request xmlns="http://tempuri.org">
<id S="ID-KMEHR" SV="1.0" SL="">Delta.PutUserLink.25/08/2017 13:07:43</id>
<author>
<hcparty>
</hcparty>
</author>
</request>
<userlink xmlns="http://tempuri.org">
<user1>
<cd S="CD-USERTYPE" SV="1.0" SL="" DN="" L="fr">patient</cd>
<patient>
<id S="ID-PATIENT" SV="1.0" SL="">97031038713</id>
</patient>
</user1>
<user2>
</user2>
<type S="CD-USERLINK" SV="1.0" SL="" DN="" L="fr">patientassent</type>
</userlink>
</PutUserLinkRequest>
I cannot retrieve the value 97031038713
XmlDocument _xmlDoc = new XmlDocument();
_xmlDoc.LoadXml(_mRec.Content);
XmlNamespaceManager manager = new XmlNamespaceManager(_xmlDoc.NameTable);
manager.AddNamespace("ns","http://tempuri.org");
Then I tried different things but without success
`<?xml version="1.0" encoding="utf-8"?>
<PutUserLinkRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<request xmlns="http://tempuri.org">
<id S="ID-KMEHR" SV="1.0" SL="">Delta.PutUserLink.25/08/2017 13:07:43</id>
<author>
<hcparty>
</hcparty>
</author>
</request>
<userlink xmlns="http://tempuri.org">
<user1>
<cd S="CD-USERTYPE" SV="1.0" SL="" DN="" L="fr">patient</cd>
<patient>
<id S="ID-PATIENT" SV="1.0" SL="">97031038713</id>
</patient>
</user1>
<user2>
</user2>
<type S="CD-USERLINK" SV="1.0" SL="" DN="" L="fr">patientassent</type>
</userlink>
</PutUserLinkRequest>
You can do this trivially with LINQ to XML:
var doc = XDocument.Parse(xml);
XNamespace ns = "http://tempuri.org";
var id = (string) doc.Descendants(ns + "patient")
.Elements(ns + "id")
.Single();
See this fiddle for a demo.

Unable to read XML correctly

I found an article to help with XML parsing:
http://geekswithblogs.net/pabothu/archive/2014/04/29/reading-a-complex-xml-using-linq-in-c-sharp.aspx.
I am trying to read the XML but I am getting a null object. I am a bit confused what I am doing wrong since I am not able to debug into those LINQ queries.
var containers =
from container in xmlDoc.Descendants("container")
//where container.Attribute("ID").Value != "0"
select new Container
{
id = Convert.ToInt32(container.Element("id").Value),
name = container.Element("name").Value,
enabled = Convert.ToBoolean(container.Element("enabled").Value),
components = new List<Component>(
from component in container.Descendants("component")
select new Component
{
id = Convert.ToInt32(component.Element("id").Value),
name = component.Element("name").Value,
type = component.Element("type").Value,
connectors = new List<Connector>(
from connector in component.Descendants("connector")
select new Connector
{
id = Convert.ToInt32(component.Element("id").Value),
name = connector.Element("name").Value,
source = connector.Element("id").Value,
destination = component.Element("id").Value
})
})
};
And here is the XML:
<?xml version="1.0" encoding="UTF-8"?>
<simplevisio>
<container>
<id>1</id>
<name>Naming</name>
<component>
<id>2</id>
<type>Server</type>
<name>First</name>
<connector>
<id>3</id>
<name>.</name>
</connector>
<connector>
<id>5</id>
<name>isShortName()</name>
</connector>
</component>
<component>
<id>3</id>
<type>Server</type>
<name>Last</name>
<connector>
<id>5</id>
<name>isShortName()</name>
</connector>
</component>
<enable>true</enable>
<connector>
<id>5</id>
<name>getFullname()</name>
</connector>
</container>
<container>
<id>4</id>
<name></name>
<component>
<id>5</id>
<type>Server</type>
<name>FirstLast</name>
</component>
<enable>false</enable>
</container>
</simplevisio>
You're querying for enabled elements, but your sample XML contains enable elements. That's why you're getting NullReferenceException.
Change
enabled = Convert.ToBoolean(container.Element("enabled").Value),
to
enabled = Convert.ToBoolean(container.Element("enable").Value),
or update your XML schema to match your query.

c# xml -> dictionary <string, Tuple<string,string,string>> in Linq

I have this XML Document
<?xml version="1.0" encoding="utf-8"?>
<Tag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<data ID="1" Tag1="A" Tag2="123" Tag3="C" />
<data ID="2" Tag1="AB" Tag2="12C3" Tag3="D" />
</Tag>
I want the convert the document into Dictionary Type <string, Tuple<string,string,string>>
So basically ID -> Tag1, Tag2, Tag3
I know there are a lot that do Key -> Value like this:
var configDictionary = xdoc.Descendants("data").ToDictionary(
datum => datum.Attribute("ID").Value,
datum => datum.Attribute("value").Value);
But i need have the value take in 3 strings.
This is rather simple. All you're missing is a declaration of the tuple using Tuple.Create:
var configDictionary = xdoc.Descendants("data")
.ToDictionary(
datum => datum.Attribute("ID").Value,
datum => Tuple.Create(datum.Attribute("Tag1").Value,
datum.Attribute("Tag2").Value,
datum.Attribute("Tag3").Value));
Note this has no validation that the attributes actually exists, which will need to be added.

Categories

Resources