How to read multiple attributes from an xml file - c#

I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<TestRun id="2fc10ef6-b97f-49e5-a58d-863dfb599cb3" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Times creation="2019-08-26T11:27:34.3642040+00:00" queuing="2019-08-26T11:27:34.3642190+00:00" start="2019-08-26T11:27:29.1640690+00:00" finish="2019-08-26T11:29:28.0320260+00:00" />
<TestSettings name="default" id="3c3c8ad0-9076-4c83-a283-03f5490f906b">
<Deployment runDeploymentRoot="_9e3d0007c2b9 2019-08-26 11:27:34" />
</TestSettings>
<Results>
<UnitTestResult testName="FirstName" outcome="Passed" testListId="1">
<Output>
</Output>
</UnitTestResult>
<UnitTestResult testName="SecondName" outcome="Passed" testListId="2">
<Output>
</Output>
</UnitTestResult>
<UnitTestResult testName="Thirdname" outcome="Passed" testListId="3">
<Output>
</Output>
</UnitTestResult>
</Results>
</TestRun>
And i have the following classes:
{
public string testName { get; set; }
public string outcome { get; set; }
}
public DtoHeader ReadXmlFile()
{
var binDirectory = Path.GetDirectoryName(GetType().GetTypeInfo().Assembly.Location);
var file = Path.Combine(binDirectory, "myfile.xml");
var xDocument = XDocument.Load(file);
XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(new NameTable());
nameSpaceManager.AddNamespace("ns", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010");
var items = xDocument.Root.XPathSelectElements("./ns:Results", nameSpaceManager).ToArray();
if (!items.Any())
{
}
return new DtoHeader
{
testName = items.Descendants().Attributes("testName").First().Value,
};
}
I will like to extract the value of the attributes testName and outcome and put these values in a list.However, I have not been able to do this after going through multiple examples.

items.Descendants().Attributes("testName") returns IEnumerable of XAttribute.
You need to select the Value member of each XAttibute and return a list.
var list = items.Descendants().Attributes("testName").Select(t => t.Value).ToList();
Edit:
Return as DtoHeader List:
var list = items.Descendants().Attributes("testName").Select(t =>
new DtoHeader
{
testName = t.Value
}
).ToList();

To get a list of DtoHeaders using Linq2Xml you could use the following approach:
var headers = xDocument.Root.XPathSelectElements("./ns:Results", nameSpaceManager)
.Elements()
.Select(x => new DtoHeader
{
testName = x.Attribute("testName").Value,
outcome = x.Attribute("outcome").Value
})
.ToList();

I suggest to use xmltocsharp to convert the xml to classes and use XmlSerializerto do the rest of the job. Here is the simplified code to get the desired result.
string requestBody = ReadFile("XMLFile1.xml");
XmlSerializer serializer = new XmlSerializer(typeof(TestRun));
using (TextReader reader = new StringReader(requestBody))
{
//convert the xml to object
TestRun testRun = (TestRun)serializer.Deserialize(reader);
foreach (var result in testRun.Results.UnitTestResult)
{
Console.WriteLine($"{result.TestName} : {result.Outcome}");
}
}

var doc = XDocument.Load(file);
var list = new System.Collections.Generic.List<DtoHeader>();
foreach (var n in doc.Descendants().First(node => node.Name.LocalName == "Results").Elements().Where(e => e.Name.LocalName == "UnitTestResult"))
{
list.Add(new DtoHeader
{
outcome = n.Attribute("outcome").Value,
testName = n.Attribute("testName").Value,
});
}

Related

How to get separate values from Xlement

This is a portion of XML I'm trying to parse
<BRTHDATES>
<BRTHDATE value="5/1/1963" code="B"/>
</BRTHDATES>
var birthdates = xmlDoc.XPathSelectElements("/INDV/PERSON/BRTHDATES").Elements().Where(e => e.Name == "BRTHDATE");
xe = birthdates.Elements().Where(e => e.Name == "BRTHDATE");
bbs = from b in birthdates
select new
{
Birthdays = b.FirstAttribute.Value,
Code = b?.Value
};
var status = birthdates.Elements().Where(e => e.Name.LocalName == "BRTHDATE").Single().Value;
When I try to get "Value" from the Element I get an empty string. I can't get anything for the "code" attribute.
It sure seems like this should be a lot easier...
You can try below code. I've already tested through a test project and got the require value.
string personBirthday = string.Empty;
string soapResult = #"<?xml version=""1.0"" encoding=""utf - 8"" ?><INDV> <PERSON> <BRTHDATES><BRTHDATE value = ""5/1/1963"" code = ""B"" /> </BRTHDATES></PERSON></INDV> ";
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(soapResult));
XmlNodeList person = doc.GetElementsByTagName("BRTHDATES");
if (person[0].ChildNodes.Count > 0)
{
foreach (XmlNode item in person[0].ChildNodes)
{
if (item.Name.Trim().Equals("BRTHDATE"))
{
personBirthday = !string.IsNullOrEmpty(item.Attributes[0].Value) ? item.Attributes[0].Value.Trim() : string.Empty;
}
}
}
Here is the solution
You can select specific Element from a Xml. Just try below sample code
XmlNodeList generalTabNodeList = xmlDocument.SelectNodes("/INDV/PERSON/BRTHDATES");
foreach (XmlNode node in generalTabNodeList)
{
if (node.ChildNodes.Count > 0)
{
string birthdate = !string.IsNullOrEmpty(node.ChildNodes[0].ToString()) ? node.ChildNodes[2].InnerText.ToString() : string.Empty;
}
}
I can't quite follow what you are trying to do, but, this should get you going:
For an XML file that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<INDV>
<PERSON>
<BRTHDATES>
<BRTHDATE value="5/1/1963" code="B"/>
</BRTHDATES>
</PERSON>
</INDV>
(Note, this is an entire XML document - one that matches your code, not just the snippet you provided (that doesn't match your code))
This code will pick out the value and code attributes:
using (var xmlStream = new FileStream("Test.xml", FileMode.Open))
{
XDocument xmlDocument = XDocument.Load(xmlStream);
var birthDateElements = xmlDocument.XPathSelectElements("/INDV/PERSON/BRTHDATES/BRTHDATE");
var birthDateElement = birthDateElements.FirstOrDefault();
if (birthDateElement != null)
{
var attributes = birthDateElement.Attributes();
var valueAttribute = attributes.Where(a => a.Name == "value");
var codeAttribute = attributes.Where(a => a.Name == "code");
}
}
You can play around with this code to figure out what you want to do. Whatever you do, don't pick out attributes by position, pick them out by name.

XML to String List

I have some code that I need to put into a string list in C# and I am reading this code from an XML files and the layout of it is something like below...
<?xml version="1.0"?>
<accountlist>
<main>
<account id="1" special_id="4923959">
<username>Adam</username>
<motto>Hello Everyone>
<money>1004</money>
<friends>394</friends>
<rareid>9</rareid>
<mission>10</mission>
</account>
</main>
</accountlist>
How can I put each account tag into a string list? from the first < account > to the < / account > tag?
Please do NOT tell me to go to the link below as it does NOT work!!
How to read a XML file and write into List<>?
So far I have tried the below code, and the string list just stays empty
XDocument doc = XDocument.Parse(this._accountsFile);
List<string> list = doc.Root.Elements("account")
.Select(element => element.Value)
.ToList();
this._accounts = list;
You'll have to use Descendants instead of Elements:
List<string> list = doc.Root.Descendants("account").Descendants()
.Select(element => element.Value)
.ToList();
Elements only returns child elements of the element (in case of the root element this means <main>).
Descendants returns the entire tree inside the element.
Also: You'll have to fix the tag <motto>Hello Everyone> to <motto>Hello Everyone</motto>
This will work on your example (but you need to close this tag <motto>Hello Everyone>
public List<string> GetAccountsAsXmlList(string filePath)
{
XmlDocument x = new XmlDocument();
x.Load(filePath);
List<string> result = new List<string>();
XmlNode currentNode;
foreach (var accountNode in x.LastChild.FirstChild.ChildNodes)
{
currentNode = accountNode as XmlNode;
result.Add(currentNode.InnerXml);
}
return result;
}
EDIT as an answer to your question:
Is there a way I can get the id and specal_id in a seperate string?
you can use currentNode.Attributes["YourAttributeName"].Value, to get the values.
assume you have class Account :
class Account
{
public string accountXml { get; set; }
public string Id { get; set; }
public string Special_id { get; set; }
}
Then :
public List<Account> GetAccountsAsXmlList(string filePath)
{
XmlDocument x = new XmlDocument();
x.Load(filePath);
List<Account> result = new List<Account>();
XmlNode currentNode;
foreach (var accountNode in x.LastChild.FirstChild.ChildNodes)
{
currentNode = accountNode as XmlNode;
result.Add(new Account
{
accountXml = currentNode.InnerXml,
Id = currentNode.Attributes["id"].Value,
Special_id = currentNode.Attributes["special_id"].Value,
});
}
return result;
}
Use XPath to get the account element first:
using System.Xml.XPath;
XDocument doc = XDocument.Parse(xml);
foreach(var account in doc.XPathSelectElements("accountlist/main/account")){
List<string> list = account.Descendants()
.Select(element => element.Value)
.ToList();
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication37
{
class Program
{
static void Main(string[] args)
{
string input =
"<?xml version=\"1.0\"?>" +
"<accountlist>" +
"<main>" +
"<account id=\"1\" special_id=\"4923959\">" +
"<username>Adam</username>" +
"<motto>" +
"Hello Everyone>" +
"<money>1004</money>" +
"<friends>394</friends>" +
"<rareid>9</rareid>" +
"<mission>10</mission>" +
"</motto>" +
"</account>" +
"</main>" +
"</accountlist>";
XDocument doc = XDocument.Parse(input);
var results = doc.Descendants("accountlist").Select(x => new {
id = x.Element("main").Element("account").Attribute("id").Value,
special_id = x.Element("main").Element("account").Attribute("special_id").Value,
username = x.Element("main").Element("account").Element("username").Value,
motto = x.Element("main").Element("account").Element("motto").FirstNode.ToString(),
money = x.Element("main").Element("account").Element("motto").Element("money").Value,
friends = x.Element("main").Element("account").Element("motto").Element("friends").Value,
rareid = x.Element("main").Element("account").Element("motto").Element("rareid").Value,
mission = x.Element("main").Element("account").Element("motto").Element("mission").Value,
}).ToList();
}
}
}

How to get all different element tags from root tag using XPath

I have a following xml file
<root>
<element1>
<header>header1</header>
<tag1>tag1</tag1>
<response>
<status>success</status>
<Data>
<id>d1</id>
<test>2</test>
</Data>
<Beta>
<betaid>sdsd</betaid>
<code>123</code>
<code>ddd</code>
</Beta>
</response>
</element1>
</root>
My Question: How to get the first child elements under "Response" tag? i.e staus, data and beta.
Using XPath in C#. Thank you
The .net code i have is here but it does not work.
XPathDocument doc= new XPathDocument(XmlReaderdata);
XPathNavigator mes, Nav = doc.CreateNavigator();
foreach(XPathNavigator node in (XPathNodeIterator)Nav.Evaluate("//response/*)
{
node.Name;
}
An XPath query like this should work:
//response/*
For example:
var xml = #"<root> ... </root>";
using (StringReader stream = new StringReader(xml))
{
XPathDocument doc= new XPathDocument(stream);
XPathNavigator nav = doc.CreateNavigator();
XPathNodeIterator itor = (XPathNodeIterator)nav.Evaluate("//response/*");
foreach(XPathNavigator node in itor)
{
Console.WriteLine(node.Name);
}
}
Produces the output:
status
Data
Beta
You can use //response:
DTO:
public class Response
{
public Response()
{
Data = new List<KeyValuePair<string, string>>();
Beta = new List<KeyValuePair<string, string>>();
}
public string Status { get; set; }
public List<KeyValuePair<string, string>> Data { get; set; }
public List<KeyValuePair<string, string>> Beta { get; set; }
}
Code:
var document = XDocument.Parse(data);
var element = document.XPathSelectElement("//response");
var response = new Response();
response.Status = element.Element("status").Value;
foreach(var dataElement in element.Element("Data").Elements())
{
response.Data.Add(new KeyValuePair<string, string>(dataElement.Name.LocalName, dataElement.Value));
}
foreach(var betaElement in element.Element("Beta").Elements())
{
response.Beta.Add(new KeyValuePair<string, string>(betaElement.Name.LocalName, betaElement.Value));
}
Edit: Partial loading
public Response ParseResponse(XPathDocument document)
{
var navigator = document.CreateNavigator();
var iterator = navigator.Select("//response");
iterator.MoveNext();
var responseDoc = XDocument.Parse(iterator.Current.OuterXml);
var element = responseDoc.Element("response");
var response = new Response();
response.Status = element.Element("status").Value;
foreach(var dataElement in element.Element("Data").Elements())
{
response.Data.Add(new KeyValuePair<string, string>(dataElement.Name.LocalName, dataElement.Value));
}
foreach(var betaElement in element.Element("Beta").Elements())
{
response.Beta.Add(new KeyValuePair<string, string>(betaElement.Name.LocalName, betaElement.Value));
}
return response;
}
Response response;
using(var reader = XmlReader.Create(stream))
{
var doc = new XPathDocument(reader);
response = ParseResponse(doc);
}
Are you trying to get the names of the child nodes?
i hope this will help
string xmlstring = "<root><element><response><status>Success</status><Data><id>1</id></Data></response></element></root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlstring);
XmlNode node = doc.SelectSingleNode("/root/element/response");
XmlNodeList children = node.ChildNodes;
foreach(XmlNode i in children )
Console.WriteLine(i.Name);

How I get the parentnode attribute name if I have the attribute name of the child node?

hi i want to get the up node from a xml. For example here a xml structure...
<feature name="mod1">
<user name="user1"></user>
<user name="user2"></user>
<user name="user3"></user>
</feature>
i have in my application the username and i want than the node feature name attribute.
XmlReader reader = XmlReader.Create(new StringReader(xml));
XElement doc = XElement.Load(reader);
int counter = 0;
foreach (XElement user in doc.Descendants("USER"))
{
try
{
row = tb.NewRow();
row["ID"] = counter++;
row["Name"] = user.Attribute("NAME").Value;
row["Host"] = user.Attribute("HOST").Value;
row["Used_Licenses"] = user.Attribute("USED_LICENSES").Value;
row["Checkout_Time"] = user.Attribute("CHECKOUT_TIME").Value;
row["Modul"] = user.Parent.Attribute("NAME").Value; //don't work :(
tb.Rows.Add(row);
}
catch (Exception)
{
}
}
you should be able to get it by
user.Parent.Attribute("NAME").Value;
this Works with :
<LM-X STAT_VERSION="3.32">
<LICENSE_PATH >
<FEATURE NAME="GlobalZoneEU" >
<USER NAME="SYSTEM" HOST="LRV171" IP="172.16.11.115" USED_LICENSES="2000" LOGIN_TIME="2013-04-17 12:42" CHECKOUT_TIME="2013-04-17 12:42" SHARE_CUSTOM="hweuser:172.16.11.115"/>
<USER NAME="pbsadmin" HOST="SERV11" IP="172.16.11.115" USED_LICENSES="720" LOGIN_TIME="2013-04-17 12:44" CHECKOUT_TIME="2013-04-17 12:44" SHARE_CUSTOM="pbsadmin:LWSERV171:1592_40960072_1356792762_826820"/>
</FEATURE>
</LICENSE_PATH>
</LM-X>
EDIT
Get the feature name by user name :
var featureNames = xDoc.Descendants("USER")
.Where(x => x.Attribute("NAME").Value == <your input>)
.Select(x => x.Parent.Attribute("NAME").Value);
var firstFeatureName = featureNames.FirstOrDefault();
Seems like the attributes are case sensitive.
XmlReader reader = XmlReader.Create(\\File Path);
XElement doc = XElement.Load(reader);
int counter = 0;
foreach (XElement user in doc.Descendants("USER"))
{
try
{
string node = user.Parent.Attribute("NAME").Value; //Working - Returning 'GlobalZoneEU'
}
catch (Exception)
{
}
}
The following code should work for you.
string xml = "<feature name=\"mod1\">";
xml += "<user name=\"user1\"> </user>";
xml += "<user name=\"user2\"> </user> ";
xml += "<user name=\"user3\"></user>";
xml += "</feature>";
XmlDocument xdoc=new XmlDocument();
xdoc.LoadXml(xml);
XDocument mydoc = XDocument.Parse(xdoc.OuterXml);
var result = mydoc.Elements("feature").Where(parent =>
parent.Elements("user").Any(child =>
child.Attribute("name").Value == "user2"));
and don't forget to include
using System.Xml;
using System.Xml.Linq;

How to use XmlSerializer to deserialize a simple collection into an instance of List<string>

I haven't been able to find a question related to my specific problem.
What I am trying to do is take a list of Xml nodes, and directly deserialize them to a List without having to create a class with attributes.
So the xml (myconfig.xml) would look something like this...
<collection>
<item>item1</item>
<item>item2</item>
<item>item3</item>
<item>etc...</item>
</collection>
In the end I would like a list of items as strings.
The code would look like this.
XmlSerializer serializer = new XmlSerializer( typeof( List<string> ) );
using (XmlReader reader = XmlReader.Create( "myconfig.xml" )
{
List<string> itemCollection = (List<string>)serializer.Deserialize( reader );
}
I'm not 100% confident that this is possible, but I'm guessing it should be. Any help would be greatly appreciated.
Are you married to the idea of using a serializer? If not, you can try Linq-to-XML. (.NET 3.5, C# 3 [and higher])
Based on your provided XML file format, this is the simple code.
// add 'using System.Xml.Linq' to your code file
string file = #"C:\Temp\myconfig.xml";
XDocument document = XDocument.Load(file);
List<string> list = (from item in document.Root.Elements("item")
select item.Value)
.ToList();
Ok, interestingly enough I may have found half the answer by serializing an existing List.
The result I got is as follows...
This following code:
List<string> things = new List<string> { "thing1", "thing2" };
XmlSerializer serializer = new XmlSerializer(typeof(List<string>), overrides);
using (TextWriter textWriter = new StreamWriter("things.xml"))
{
serializer.Serialize(textWriter, things);
}
Outputs a result of:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>thing1</string>
<string>thing2</string>
</ArrayOfString>
I can override the root node by passing an XmlAttributeOverrides instance to the second parameter of the XmlSerializer constructor. It is created like this:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attributes = new XmlAttributes { XmlRoot = new XmlRootAttribute("collection") };
overrides.Add( typeof(List<string>), attributes );
This will change "ArrayOfString" to "collection". I still have not figured out how to control the name of the string element.
To customize List element names using XmlSerializer, you have to wrap the list.
[XmlRoot(Namespace="", ElementName="collection")]
public class ConfigWrapper
{
[XmlElement("item")]
public List<string> Items{ get; set;}
}
Usage:
var itemsList = new List<string>{"item1", "item2", "item3"};
var cfgIn = new ConfigWrapper{ Items = itemsList };
var xs = new XmlSerializer(typeof(ConfigWrapper));
string fileContent = null;
using (var sw = new StringWriter())
{
xs.Serialize(sw, cfgIn);
fileContent = sw.ToString();
Console.WriteLine (fileContent);
}
ConfigWrapper cfgOut = null;
using (var sr = new StringReader(fileContent))
{
cfgOut = xs.Deserialize(sr) as ConfigWrapper;
// cfgOut.Dump(); //view in LinqPad
if(cfgOut != null)
// yields 'item2'
Console.WriteLine (cfgOut.Items[1]);
}
Output:
// fileContent:
<?xml version="1.0" encoding="utf-16"?>
<collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<item>item1</item>
<item>item2</item>
<item>item3</item>
</collection>
If you don't want to wrap the list, the DataContractSerializer will allow you to custom name the elements if you subclass it:
[CollectionDataContract(Name = "collection", ItemName = "item", Namespace = "")]
public class ConfigWrapper : List<string>
{
public ConfigWrapper() : base() { }
public ConfigWrapper(IEnumerable<string> items) : base(items) { }
public ConfigWrapper(int capacity) : base(capacity) { }
}
Usage And Output:
var cfgIn = new ConfigWrapper{ "item1", "item2", "item3" };
var ds = new DataContractSerializer(typeof(ConfigWrapper));
string fileContent = null;
using (var ms = new MemoryStream())
{
ds.WriteObject(ms, cfgIn);
fileContent = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine (fileContent);
}
// yields: <collection xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><item>item1</item><item>item2</item><item>item3</item></collection>
ConfigWrapper cfgOut = null;
using (var sr = new StringReader(fileContent))
{
using(var xr = XmlReader.Create(sr))
{
cfgOut = ds.ReadObject(xr) as ConfigWrapper;
// cfgOut.Dump(); //view in LinqPad
if(cfgOut != null)
// yields 'item2'
Console.WriteLine (cfgOut[1]);
}
}

Categories

Resources