This is xml that I receive:
<?xml version="1.0" encoding="UTF-8"?>
<mdpr:Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mdpr="http://...">
<mdpr:contactList>
<mdpr:contact ID="{123456}" classID="Customer">
<mdpr:Name>data1</mdpr:Name>
<mdpr:TransportCode>data2</mdpr:TransportCode>
<mdpr:ZipCode>data3</mdpr:ZipCode>
<mdpr:City>data4</mdpr:City>
</mdpr:contact>
<mdpr:contact ID="{234567}" classID="Customer">
<mdpr:Name>data5</mdpr:Name>
<mdpr:TransportCode>data6</mdpr:TransportCode>
<mdpr:ZipCode>data7</mdpr:ZipCode>
<mdpr:City>data8</mdpr:City>
</mdpr:contact>
</mdpr:contactList>
...
Here is how I try to get all contacts:
public class Contact
{
public string Name { get; set; }
public string TransportCode { get; set; }
}
...
XDocument xdoc = XDocument.Load(doc.CreateNavigator().ReadSubtree());
List<Contact> contacts = (from xml in xdoc.Elements("contactList").Elements("contact")
select new Contact
{
Name = xml.Element("Name").Value,
TransportCode = xml.Element("TransportCode").Value
}).ToList();
But I get nothing. What am I doing wrong here?
You have mdpr namespace declared in your xml:
xmlns:mdpr="http://..."
but you are providing only local name of elements in query. E.g. you provide contactList name, but full name of element is mdpr:contactList. That's why nothing is found.
You should define XNamespace for your namespace and use it to create full names of elements:
XNamespace mdpr = "http://...";
var contacts = from c in xdoc.Root.Element(mdpr + "contactList")
.Elements(mdpr + "contact")
select new Contact {
TransportCode = (string)c.Element(mdpr + "TransportCode"),
Name = (string)c.Element(mdpr + "Name")
};
Also contactList is not the root of document. You should search it under Root.
XDocument xdoc = XDocument.Load(doc.CreateNavigator().ReadSubtree());
var ns = xdoc.Root.Name.Namespace;
List<Contact> contacts = (from xml in xdoc.Root.Elements(ns +"contactList").Elements(ns +"contact")
select new Contact
{
Name = xml.Element(ns +"Name").Value,
TransportCode = xml.Element(ns +"TransportCode").Value
}).ToList();
try to specify namespace
string nmsp = "http://www.w3.org/2001/XMLSchema-instance/"
from xml in xdoc.Elements(nmsp+"contactList").Elements(nmsp + "contact")
Related
List:
List<string> list = new List<string>();
XML File:
<memberlist>
<member>
<name>Name</name>
<status>Status</status>
</member>
</memberlist>
How would I go about parsing this file, lets say (file.xml) into the list?
I've tried many ways but none of them seem to be working.
I want to check to see if the status is 'gold', and if it is, I would like to put the name of that member into the list.
Load the xml into an XDocument. You can do this from a file, I have demonstrated from a string.
Do a linq query for elements named "status" (within "member") that also have a value of "gold".
Of those, grab the value of the "name" element.
Use AddRange on your list of strings.
List<string> lstGolds = new List<string>();
string xml ="<memberlist><member><name>Name</name><status>gold</status></member></memberlist>";
XDocument doc = XDocument.Parse(xml);
var goldStatus = doc.Descendants("member")
.Where(d => d.Element("status").Value == "gold")
.Select(d => d.Element("name").Value);
lstGolds.AddRange(goldStatus);
You should use using System.Xml.Linq;
using (FileStream fileStramWrite = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader streamReader = new StreamReader(fileStramWrite))
{
var xdoc = XDocument.Load(streamReader);
var xElement = xdoc.Element("memberlist");
var xElement2 = xElement.Element("member");
var name = xElement2.Element("name").Value;
var status = xElement2.Element("status").Value;
}
Convert your XML to a list of objects....
Use these two mehods:
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new ISOEncodingStringWriter();
var serializer = new XmlSerializer(typeof(T));
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
serializer.Serialize(stringwriter, dataToSerialize, xns);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
Make the following Class where you give the following attributes
[XmlRoot(ElementName = "Members", Namespace = "")]
public class Members
{
[XmlElement("Member")]
public Member[] member { get; set; }
}
public class Member
{
[XmlElementAttribute("Name")]
public string Name { get; set; }
[XmlElementAttribute("Status")]
public string Status { get; set; }
}
Then deserialize your data.
Members result = Deserialize<Members>(XML_String);
You got your members in a array and you ca convert that to a list if you want.
You can use LINQ
XElement xml = XElement.Load("locationToYourXmlFile");
var members = xml.Elements("member");
var list = members.Where(member => member.Element("status")?.Value == "Gold")
.Select(member => member.Element("name")?.Value)
.ToList();
Assuming your XML has a bunch of member elements under the root memberlist element, each with a name and status element inside that.
<?xml version="1.0" encoding="utf-8"?>
<memberlist>
<member>
<name>John</name>
<status>Gold</status>
</member>
<member>
<name>Sam</name>
<status>Silver</status>
</member>
<member>
<name>Sara</name>
<status>Gold</status>
</member>
</memberlist>
I have a xml that looks like this
<?xml version="1.0" encoding="utf-8" ?>
<Resorces>
<Resource id="3" name="loreum ipsum" downloadcount="5"></Resource>
<Resource id="2" name="loreum ipsum" downloadcount="9"></Resource>
</Resorces>
I have a class
public class Report
{
public int ResourceId {get; set; }
public string ResourceName { get; set; }
public int DownloadCount { get; set; }
}
I want to convert that xml into list of Report objects
I tried the below code
var resourceList = doc.Descendants("Resorces")
.First()
.Elements("Resource")
.ToList();
I get values like this,
How can I get it as list of objects?
Basically what you are missing is the part where you convert the Xml objects into your defined object of Report. This is how you can do it:
var resourceList = doc.Descendants("Resorces")
.First()
.Elements("Resource")
.Select(element => new Report()
{
ResourceId = (int)element.Attribute("id"),
ResourceName = (string)element.Attribute("name"),
DownloadCount = (int)element.Attribute("downloadcount")
})
.ToList();
I kept here the previous linq methods you called to keep it close to the original but as others said you can just get the Elements("Resource") from the doc root
XmlDocument newdoc = new XmlDocument();
newdoc.InnerXml = " <?xml version="1.0" encoding="utf-8" ?>
<Resorces>
<Resource id="3" name="loreum ipsum" downloadcount="5"></Resource>
<Resource id="2" name="loreum ipsum" downloadcount="9"></Resource>
</Resorces>";
List<string> list = new List <string>();
var selectnode = "Resorces/Resource";
var nodes = newdoc.SelectNodes(selectnode);
foreach (XmlNode nod in nodes)
{
string id = nod["id"].InnerText;
string name = nod["name"].InnerText;
string downloadcount = nod["downloadcount"].InnerText;
list.Add(id);
list.Add(name);
list.Add(downloadcount);
}
Console.WriteLine(list.Count);
You could use Elements method to get elements for a given element name.
XDocument doc = XDocument.Load(filepath);
var result = doc.Root
.Elements("Resource")
.Select(x=> new Report()
{
ResourceId = int.Parse( x.Attribute("id").Value),
ResourceName = (string)x.Attribute("name").Value,
DownloadCount = int.Parse( x.Attribute("downloadcount").Value)
})
.ToList();
Check this Demo
You can get by this
XDocument doc = XDocument.Load(xmlpath);
List<Report> resourceList = doc.Descendants("Resorces")
.First()
.Elements("Resource")
.Select(report => new Report()
{
ResourceId = (int)report.Attribute("id"),
ResourceName = (string)report.Attribute("name"),
DownloadCount = (int)report.Attribute("downloadcount")
}).ToList();
The sample below is parsing an XML document then looping through the members and storing them in a list of objects (The data ultimately ends up in an SQL database):
public static void Parse(XDocument xml)
{
XNamespace ns = "http://somenamespace.com/ns";
var Locations =
from Continents in xml.Descendants(ns + "Continent")
from Countries in Continents.Elements(ns + "Country")
select new
{
Continent1 = (string) Continents.Element(ns + "Europe"),
Country1 = (string) Countries.Element(ns + "United_Kingdom"),
Cities = from Cities in Countries.Elements(ns + "City")
select new
{
City1 = (string) Cities.Element(ns + "London")
}
};
List<Location> locationColl = new List<Location>();
loc_Entity_FrameworkContainer context = new loc_Entity_FrameworkContainer();
var i = 0;
foreach (var location in Locations)
{
Location l = new Location();
locationColl.Add(l);
locationColl[i].Continent = (string) location.Continent1;
locationColl[i].Country = (string) location.Country1;
locationColl[i].City = (string) location.City1; // Can't access "City1"
context.Location.Add(locationColl[i]);
i++;
}
context.SaveChanges();
}
The statement: locationColl[i].City = (string)location.City1;
doesn't find "City1". (This is the issue, I can't access all the members from "Locations" in one loop)
Location Class:
namespace locationProject
{
using System;
using System.Collections.Generic;
public partial class Location
{
public string Continent { get; set; }
public string Country { get; set; }
public string City { get; set; }
}
}
XML Example:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:ns="http://somenamespace.com/ns">
<ns:Continent>
<ns:Europe>21c99a56-4b3d-4571-802a-76cdb6b81a01</ns:Europe>
<ns:Country>
<ns:United_Kingdom>eb2e9eec-dc3b-4636-bcf5-dba0024e62f3</ns:United_Kingdom>
<ns:City>
<ns:London>109b48ec-d829-4a87-b200-4dc9a94db48c</ns:London>
</ns:City>
</ns:Country>
</ns:Continent>
<ns:Continent>
<ns:Europe>a11ed925-dc0d-4dfd-b1c2-52eb697ad689</ns:Europe>
<ns:Country>
<ns:United_Kingdom>a61d02ef-7b80-4390-926a-49c6d9af9634</ns:United_Kingdom>
<ns:City>
<ns:London>dbb9c5cc-b08f-4223-b32c-acb4ed9ce97c</ns:London>
</ns:City>
</ns:Country>
</ns:Continent>
</feed>
I'm trying to find a way of looping through all the elements (Continent1, Country1, City1) that doesn't involve multiple loops and doesn't break the nested structure of the LINQ statements.
There are questions on here similar to this one, but I haven't found one I understand well enough to integrate with my code.
Thanks a lot!
Your anonymous type contained in the Locations list has a .Cities property that contains a City1 member:
Cities = from Cities in Countries.Elements(ns + "City")
select new
{
City1 = (string) Cities.Element(ns + "London")
}
Try this:
var Locations =
from Continents in xml.Descendants(ns + "Continent")
from Countries in Continents.Elements(ns + "Country")
from Cities in Countries.Elements(ns + "City")
select new
{
Continent1 = (string) Continents.Element(ns + "Europe"),
Country1 = (string) Countries.Element(ns + "United Kingdom"),
City1 = (string) Cities.Element(ns + "London")
};
<?xml version="1.0" standalone="yes"?>
<CompanyInfo>
<Employee name="Jon" deptId="123">
<Region name="West">
<Area code="96" />
</Region>
<Region name="East">
<Area code="88" />
</Region>
</Employee>
</CompanyInfo>
public class Employee
{
public string EmployeeName { get; set; }
public string DeptId { get; set; }
public List<string> RegionList {get; set;}
}
public class Region
{
public string RegionName { get; set; }
public string AreaCode { get; set; }
}
I am trying to read this XML data, so far I have tried this:
XDocument xml = XDocument.Load(#"C:\data.xml");
var xElement = xml.Element("CompanyInfo");
if (xElement != null)
foreach (var child in xElement.Elements())
{
Console.WriteLine(child.Name);
foreach (var item in child.Attributes())
{
Console.WriteLine(item.Name + ": " + item.Value);
}
foreach (var childElement in child.Elements())
{
Console.WriteLine("--->" + childElement.Name);
foreach (var ds in childElement.Attributes())
{
Console.WriteLine(ds.Name + ": " + ds.Value);
}
foreach (var element in childElement.Elements())
{
Console.WriteLine("------->" + element.Name);
foreach (var ds in element.Attributes())
{
Console.WriteLine(ds.Name + ": " + ds.Value);
}
}
}
}
This enables me to get each node, its attribute name and value and so I can save these data into the relevant field in database, but this seems a long winded way and
not flexible, for instance if the XML structure changes all those foreach statements needs revisiting, also it is difficult to filter the data this way,
I need to write certain if statements to filter the data (e.g get employees from West only etc...)
I was looking for a more flexible way, using linq, something like this:
List<Employees> employees =
(from employee in xml.Descendants("CompanyInfo")
select new employee
{
EmployeeName = employee.Element("employee").Value,
EmployeeDeptId = ?? get data,
RegionName = ?? get data,
AreaCode = ?? get data,,
}).ToList<Employee>();
But I am not sure how I can get the values from the child nodes and apply the filtering (to get the certain employees only). Is this possible? Any help is appreciated.
Thanks
var employees = (from e in xml.Root.Elements("Employee")
let r = e.Element("Region")
where (string)r.Attribute("name") == "West"
select new Employee
{
EmployeeName = (string)e.Attribute("employee"),
EmployeeDeptId = (string)e.Attribute("deptId"),
RegionName = (string)r.Attribute("name"),
AreaCode = (string)r.Element("Area").Attribute("code"),
}).ToList();
But it will still require query revision when XML file structure changes.
Edit
Query for multiple regions per employee:
var employees = (from e in xml.Root.Elements("Employee")
select new Employee
{
EmployeeName = (string)e.Attribute("employee"),
DeptId = (string)e.Attribute("deptId"),
RegionList = e.Elements("Region")
.Select(r => new Region {
RegionName = (string)r.Attribute("name"),
AreaCode = (string)r.Element("Area").Attribute("code")
}).ToList()
}).ToList();
You can then filter the list for employees from given region only:
var westEmployees = employees.Where(x => x.RegionList.Any(r => r.RegionName == "West")).ToList();
You can track the structure:
from employee in xml
.Element("CompanyInfo") // must be root
.Elements("Employee") // only directly children of CompanyInfo
or less strictly
from employee in xml.Descendants("Employee") // all employees at any level
And then get the information you want:
select new Employee
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
RegionName = employee.Element("Region").Attribute("name").Value,
AreaCode = employee.Element("Region").Element("Area").Attribute("code").Value,
}
And with the additional info about multiple regions, assuming a List<Region> Regions property:
select new Employee
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
//RegionName = employee.Element("Region").Attribute("name").Value,
//AreaCode = employee.Element("Region").Element("Area").Attribute("code").Value,
Regions = (from r in employee.Elements("Region") select new Region
{
Name = r.Attribute("name").Value,
Code = r.Element("Area").Attribute("code").Value,
}).ToList();
}
You can do the selection in one query and then the filtering in second or combine them both to one query:
Two queries:
// do te transformation
var employees =
from employee in xml.Descendants("CompanyInfo").Elements("Employee")
select new
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
Regions = from region in employee.Elements("Region")
select new
{
Name = region.Attribute("name").Value,
AreaCode = region.Element("Area").Attribute("code").Value,
}
};
// now do the filtering
var filteredEmployees = from employee in employees
from region in employee.Regions
where region.AreaCode == "96"
select employee;
Combined one query (same output):
var employees2 =
from selectedEmployee2 in
from employee in xml.Descendants("CompanyInfo").Elements("Employee")
select new
{
EmployeeName = employee.Attribute("name").Value,
EmployeeDeptId = employee.Attribute("deptId").Value,
Regions = from region in employee.Elements("Region")
select new
{
Name = region.Attribute("name").Value,
AreaCode = region.Element("Area").Attribute("code").Value,
}
}
from region in selectedEmployee2.Regions
where region.AreaCode == "96"
select selectedEmployee2;
But there is one little thing you should consider adding. For robustness, you need to check existence of your elements and attributes then the selection will look like that:
var employees =
from employee in xml.Descendants("CompanyInfo").Elements("Employee")
select new
{
EmployeeName = (employee.Attribute("name") != null) ? employee.Attribute("name").Value : string.Empty,
EmployeeDeptId = (employee.Attribute("deptId") != null) ? employee.Attribute("deptId").Value : string.Empty,
Regions = (employee.Elements("Region") != null)?
from region in employee.Elements("Region")
select new
{
Name = (region.Attribute("name")!= null) ? region.Attribute("name").Value : string.Empty,
AreaCode = (region.Element("Area") != null && region.Element("Area").Attribute("code") != null) ? region.Element("Area").Attribute("code").Value : string.Empty,
}
: null
};
Here is the sample XML that I am receiving from the web service.
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
xmlns:rs='urn:schemas-microsoft-com:rowset'
xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
<s:ElementType name='row' content='eltOnly'>
<s:AttributeType name='NAME' rs:number='1'>
<s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='25'
rs:maybenull='false'/>
</s:AttributeType>
<s:AttributeType name='DESCRIPTION' rs:number='2' rs:nullable='true'
rs:writeunknown='true'>
<s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='80'/>
</s:AttributeType>
<s:extends type='rs:rowbase'/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row NAME='JOE BLOW' DESCRIPTION='PROGRAMMER'/>
<z:row NAME='ANN SMITH' DESCRIPTION='FRONT DESK'/>
<z:row NAME='STEVE JOHNSON' DESCRIPTION='TESTER'/>
</rs:data>
</xml>
I'm not sure how to go about getting to the <rs:data> node. I eventually want to enumerate through each <z:row> and get the attributes NAME and DESCRPTION. The webservice returns this to me as a string so I load the string into an XMLDocument like this:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xmlString);
Now, I'm not sure if I need to use a XmlNamespaceManager in order to tel my xDoc to import the <s:Schema>
I've tried to select a single node with an XPath with no luck because of the prefix rs.
XmlNode xNode = xDoc.SelectSingleNode("/rs:data");
If there is any further information needed, please ask.
If you are taking the xml as a string try parsing it like instead of using Load()
var xml = XDocument.Parse(xmlstring);
You have to use the namespace to get hold of the data properly.
XNamespace ns = "#RowsetSchema";
foreach (var element in xml.Descendants().Elements(ns + "row"))
{
Console.WriteLine ("Name = " + element.Attribute("NAME").Value + ", " + "Description = " + element.Attribute("DESCRIPTION").Value);
}
Using xml with linq it can be done like this:
XDocument xmlDoc = new XDocument();
xmlDoc = XDocument.Parse(xml);
XNamespace ns = "#RowsetSchema";
var namesDescs = from namesDesc in xmlDoc.Descendants().Elements(ns + "row")
select new
{
name = namesDesc.Attribute("NAME").Value,
description = namesDesc.Attribute("DESCRIPTION").Value,
};
Here's some classes for handling your xml, using these extensions: http://searisen.com/xmllib/extensions.wiki
public class Test
{
XElement self;
public Test(XElement self)
{
this.self = self;
}
public RSDataRow[] DataRows
{
get
{
if (null == _DataRows)
{
_DataRows = self.GetEnumerable("rs:data/z:row", xrow => new RSDataRow(xrow)).ToArray();
}
return _DataRows;
}
}
RSDataRow[] _DataRows;
}
[DebuggerDisplay("{Name}")]
public class RSDataRow
{
XElement self;
public RSDataRow(XElement self)
{
this.self = self;
}
public string Name
{
get { return self.Get("NAME", string.Empty); }
}
public string Description
{
get { return self.Get("DESCRIPTION", string.Empty); }
}
}
You use it like:
Test test = new Test(XElement.Parse(xmlstring));
foreach (RSDataRow row in test.DataRows)
{
string name = row.Name;
string desc = row.Description;
}