Linq to XML simple get attribute from node statement - c#

Here's the code snippet:
XDocument themes = XDocument.Load(HttpContext.Current.Server.MapPath("~/Models/Themes.xml"));
string result = "";
var childType = from t in themes.Descendants()
where t.Attribute("name").Value.Equals(theme)
select new { value = t.Attribute("type").Value };
foreach (var t in childType) {
result += t.value;
}
return result;
and here's the XML:
<?xml version="1.0" encoding="utf-8" ?>
<themes>
<theme name="Agile">
<root type="Project">
<node type="Iteration" >
<node type="Story">
<node type="Task"/>
</node>
</node>
</root>
</theme>
<theme name="Release" >
<root type="Project">
<node type="Release">
<node type="Task" />
<node type="Defect" />
</node>
</root>
</theme>
</themes>
What am I doing wrong? I keep getting an "object not set to an instance of an object" exception.
What I'm trying to return is the type of the selected node based on the type of a parent node, i.e., if the theme is "Agile" and the parent node is "Project" then the return value should be "Iteration". That's the final outcome but I never got that far because I got stuck with what you see above.

I think you want something closer to this:
XDocument themes = XDocument.Load(HttpContext.Current.Server.MapPath("~/Models/Themes.xml"));
string result = "";
var childType = from t in themes.Descendants("theme")
where t.Attribute("name").Value.Equals(theme)
select new { value = t.Descendants().Attribute("type").Value };
foreach (var t in childType) {
result += t.value;
}
return result;
EDIT:
Based on your additional info, perhaps this is even closer:
XDocument themes = XDocument.Load(HttpContext.Current.Server.MapPath("~/Models/Themes.xml"));
string result = "";
var childType = from t in themes.Descendants("theme")
where t.Attribute("name").Value.Equals(theme)
where t.Element("node").Attribute("type").Value == parent
select new { value = t.Descendants().Attribute("type").Value };
foreach (var t in childType) {
result += t.value;
}
return result;
EDIT 2: This is working for me:
XDocument themes = XDocument.Load(HttpContext.Current.Server.MapPath("~/Models/Themes.xml"));
string result = "";
var theme = "Agile";
var parent = "Project";
var childType = from t in themes.Descendants("theme")
where t.Attribute("name").Value.Equals(theme)
where t.Element("root").Attribute("type").Value == parent
from children in t.Element("root").Descendants()
select new { value = children.Attribute("type").Value };
foreach (var t in childType) {
result += t.value;
}
return result;
EDIT 3: Here's the complete working program, just throw it in a class in a console application.
static void Main(string[] args)
{
var xml = "<themes><theme name='Agile'><root type='Project'><node type='Iteration' ><node type='Story'><node type='Task'/></node></node></root></theme><theme name='Release' ><root type='Project'><node type='Release'><node type='Task' /><node type='Defect' /></node></root></theme></themes>";
XDocument themes = XDocument.Parse(xml);
string result = "";
var theme = "Agile";
var parent = "Project";
var childType = from t in themes.Descendants("theme")
where t.Attribute("name").Value.Equals(theme)
where t.Element("root").Attribute("type").Value == parent
from children in t.Element("root").Descendants()
select new { value = children.Attribute("type").Value };
foreach (var t in childType)
{
Console.WriteLine(t.value);
}
Console.Read();
}

Ok here's what I did in the end, it's not very pretty but it works.
Its based on the answer by Pramodh with a few tweeks.
string result = "";
var childType = themes.Descendants("theme")
.Where(x => x.Attribute("name").Value == theme)
.Where(x => x.Descendants("node").First().Attribute("type").Value == parentType)
.Select(x => x.Descendants("node").First().Descendants().First().Attribute("type").Value);
foreach (var t in childType) {
result += t;
}
return result;
now if I pass in theme "Agile" and parent "Iteration" it returns story.
Like I said not very pretty but it works.
Thank you to everyone who posted answers.

There are two errors in your query.
First one is:
from t in themes.Descendants()
This will give you all themes elements.
If you want the theme elements, you must specify it, by filtering like:
from t in themes.Descendants("theme")
The second error will consequently be
select new { value = t.Attribute("type").Value }
because t will be a theme element, with no attribute "type".
I can't help on this one because it is not clear what the final result must be.
Edit: According to the added informations, this should do the trick:
var childType =
from t in doc.Descendants("theme")
where t.Attribute("name").Value.Equals(theme)
select t.Element("root")
.Element("node")
.Attribute("type").Value;
However, this is meant to retrieve multiple types from nodes with the same theme name.
If the type will always be the same, you should consider calling .SingleOrDefault() .

I'm not cleared about your question. Here is my solution( as i understood)
var childType = themes.Descendants("theme")
.Where(X => X.Attribute("name").Value == "Agile")
.Where(X => X.Descendants("root").Attributes("type").First().Value == "Project")
.Select(X => X.Descendants("node").Attributes("type").First().Value);

Related

Linq to XML Find a an attribute and return seperate attribute value

<?xml version="1.0" ?>
<aliasSection>
<aliases>
<clear />
<add
name="MAIN"
server="JAG8MTO\SQLEXPRESS"
database="RMain"
trustedConnection="false" />
<add
name="DEMO"
server="JAG8MTO\SQLEXPRESS"
database="RDemo"
trustedConnection="false" />
</aliases>
</aliasSection>
In the above xml doc I need to search for an alias name then return the server and database attributes.
This is my first time working with xml docs and I'm having a hard time wrapping my head around it.
So far I have this, I can find the aliases section but I'm at a loss have to proceed from here.
public void Read(string fileName)
{
XDocument doc = XDocument.Load(fileName);
foreach (XElement el in doc.Root.Elements())
{
foreach (XAttribute attr in el.Attributes())
if (attr.ToString() == "aliases")
{
foreach (XElement element in el.Elements())
listBox1.Items.Add(element.Name + " " + element.Value);
foreach (XAttribute attrib in el.Attributes())
listBox1.Items.Add(attrib.Value);
}
}
}
aliases is element, not attribute
var document = XDocument.Load(fileName);
var data =
document.Descendants("aliases") // Select all <aliases> elements
.SelectMany(alias => alias.Elements("add")) // select all <add> elements
.Select(add => new
{
Name = add.Attribute("name").Value
Server = add.Attribute("server").Value
})
.ToList();
listBox1.Items.AddRange(data);
data will contain selected values of all <add> elements.
foreach (var item in data)
{
Console.WriteLine($"Name: {item.Name}, Server: {item.Server}");
}
// Load the xml into XElement
XDocument doc = XDocument.Load("F:\\db.xml",LoadOptions.None);
// Search through descendants and
// find one with name as MAIN
XElement result = doc.Descendants("add")
.FirstOrDefault(y => y.Attribute("name") != null &&
y.Attribute("name").Value == "MAIN");
// Get the values if valid element is found
if(result != null)
{
string server = (result.Attribute("server") != null) ? result.Attribute("server").Value : string.Empty;
string database = (result.Attribute("database") != null) ? result.Attribute("database").Value : string.Empty;
}

Need help retrieving XML data using Linq

I am trying to retrieve data from an XML file and return the parsed data in a list. Depending on what I use to access the data (Element or Attributes) I either get null (in case of Element) or something I cannot decipher (in case of Attributes).
XML Looks like this:
<DATA_RESPONSE>
<HEADER>
<MSGID>IS20101P:091317125610:98::34:0</MSGID>
</HEADER>
<DATA>
<ROW ID='IS20101P' PE_NAME='APP-029' PE_ID='4' CODE='4829' DATA='5,1,500,1' />
<ROW ID='IS20101P' PE_NAME='APPS-029' PE_ID='4' CODE='4829' DATA='4,1,500,1' />
...
</DATA>
<SUMMARY>
</SUMMARY>
<ERRORS>
</ERRORS>
</DATA_RESPONSE>
I am using the following to get the data. I read the file and store XML in a string and call a method with this string as argument:
public static Hashtable GetIDSData(string sXMLString)
{
Hashtable result = new Hashtable();
result.Add("Success", false);
result.Add("ErrorMessage", "");
result.Add("ID", "");
result.Add("PE_NAME", "");
result.Add("PE_ID", "");
result.Add("CODE", "");
result.Add("DATA", "");
xmlDoc.InnerXml = sXMLString;
XmlElement root = xmlDoc.DocumentElement;
XDocument doc = XDocument.Parse(sXMLString);
XmlNode node = xmlDoc.SelectSingleNode("DATA_RESPONSE/DATA");
if (node != null)
{
var AddressInfoList = doc.Root.Descendants("ROW").Select(Address => new
{
ID = Address.Attributes("ID")?.ToString(),
PEName = Address.Attributes("PE_NAME")?.ToString(),
PEID = Address.Attributes("PE_ID")?.ToString(),
Code = Address.Attributes("CODE")?.ToString(),
Data = Address.Attributes("DATA")?.ToString(),
}).ToList();
foreach (var AddressInfo in AddressInfoList)
{
if (string.IsNullOrEmpty(AddressInfo.Code))
{
result["Success"] = false;
result["ErrorMessage"] = "Invalid Code; code is empty.";
}
else
{
result["Success"] = true;
result["ErrorMessage"] = "";
result["ID"] = AddressInfo.ID;
result["PE_NAME"] = AddressInfo.PEName;
result["PE_ID"] = AddressInfo.PEID;
result["CODE"] = AddressInfo.Code;
result["DATA"] = AddressInfo.Data;
}
}
return result;
}
In Linq section, if I use Address.Element("ID").Value, I get null returned.
There is no namespace used in XML.
First off, the GetIDSData() method does not compile as is, because at the line xmlDoc.InnerXml = sXMLString, xmlDoc has not been defined.
I'm assuming you want xmlDoc to be an XmlDocument loaded with the contents of the sXMLString parameter, so I'm changing that line to:
XmlDocument xmlDoc = new XmlDocument {InnerXml = sXMLString};
Also, your root variable is never used, so I removed it for clarity.
Now as for the main part of your question, given your current syntax, you are calling .ToString() on a collection of attributes, which is obviously not what you want. To fix this, when you're iterating the AddressInfoList, You want to fetch the attribute values like:
ID = Address.Attributes("ID")?.Single().Value
or
ID = address.Attribute("ID")?.Value
...rather than Address.Attributes("ID")?.ToString() as you have above.
You are not selecting values of attributes. In your code you are selecting attributes. Not sure what are you trying to achieve, but here is my modified version of your code that loads all elements into DataTable
public static DataTable GetIDSData(string sXMLString)
{
DataTable result = new DataTable();
result.Columns.Add("Success");
result.Columns.Add("ErrorMessage");
result.Columns.Add("ID");
result.Columns.Add("PE_NAME");
result.Columns.Add("PE_ID");
result.Columns.Add("CODE");
result.Columns.Add("DATA");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.InnerXml = sXMLString;
XmlElement root = xmlDoc.DocumentElement;
XDocument doc = XDocument.Parse(sXMLString);
XmlNode node = xmlDoc.SelectSingleNode("DATA_RESPONSE/DATA");
if (node != null)
{
var AddressInfoList = doc.Root.Descendants("ROW").Select(Address => new
{
ID = Address.Attributes("ID").Select(i=>i.Value) ,
PEName = Address.Attributes("PE_NAME").Select(i=>i.Value),
PEID = Address.Attributes("PE_ID").Select(i=>i.Value),
Code = Address.Attributes("CODE").Select(i=>i.Value),
Data = Address.Attributes("DATA").Select(i=>i.Value),
}).ToList();
AddressInfoList.ForEach(e =>
{
e.Code.ToList().ForEach(c =>
{
DataRow row = result.NewRow();
if (!string.IsNullOrEmpty(c))
{
row["Success"] = true;
row["ErrorMessage"] = "";
row["ID"] = e.ID.First();
row["PE_NAME"] = e.PEName.First();
row["PE_ID"] = e.PEID.First();
row["CODE"] = e.Code.First();
row["DATA"] = e.Data.First();
}
else
{
row["Success"] = false;
row["ErrorMessage"] = "Invalid Code; code is empty.";
}
result.Rows.Add(row);
});});
result.Dump();
return result;
}
return result;
}
And this is the result that you will get in your datatable.
ID = Address.Attributes("ID")?.ToString(),
You want to use Attribute(name) (without s) instead:
ID = Address.Attributes("ID")?.Value,

How to set a value in XML using C#?

How to change the value of sourcePatientInfo in the following xml file using c#.
I can able to read the value using,
var elem = (from n in xml.Descendants("Slot")
where n.Attribute("name").Value == "sourcePatientInfo"
select n).FirstOrDefault();
How to change the same using C#?
<?xml version="1.0" encoding="utf-8"?>
<rs:SubmitObjectsRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rs="urn:oasis:names:tc:ebxml-regrep:registry:xsd:2.1" xmlns:rim="urn:oasis:names:tc:ebxml-regrep:rim:xsd:2.1" xmlns="urn:oasis:names:tc:ebxml-regrep:rim:xsd:2.1">
<LeafRegistryObjectList>
<ObjectRef id="urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d" />
<ExtrinsicObject id="Document01" mimeType="application/dicom" objectType="urn:uuid:7edca82f-054d-47f2-a032-9b2a5b5186c1">
<Name>
<LocalizedString value="Physical" />
</Name>
<Description />
<Slot name="sourcePatientId">
<ValueList>
<Value>pid1^^^&1.2.3&ISO</Value>
</ValueList>
</Slot>
<Slot name="sourcePatientInfo">
<ValueList>
<Value>PID-3|pid1^^^&1.2.3&ISO</Value>
<Value>PID-5|Doe^John^^^</Value>
<Value>PID-7|19560527</Value>
<Value>PID-8|M</Value>
<Value>PID-11|100 Main St^^Metropolis^Il^44130^USA</Value>
</ValueList>
</Slot>
I would like to change the values using c#. Am not able to figure out the way. Any Help to resolve this issue will be appreciated.
I want to change the
<Slot name="sourcePatientInfo">
<ValueList>
<Value>PID-3|pid1^^^&1.2.3&ISO</Value>
<Value>PID-5|Doe^John^^^</Value>
to the following value
<Slot name="sourcePatientInfo">
<ValueList> <Value>PID-3|MyPID</Value>
<Value>PID-5|MyName</Value>
I have also tried the following the code,
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc1.NameTable);
namespaceManager.AddNamespace("rs", "urn:oasis:names:tc:ebxml-regrep:registry:xsd:2.1");
var query = "/rs:SubmitObjectsRequest/LeafRegistryObjectList/ExtrinsicObject";
XmlNodeList nodeList = xmlDoc1.SelectNodes(query, namespaceManager);
foreach (XmlNode node1 in nodeList)
{
if (node1.Attributes["Slot"].Value == "sourcePatientInfo")
{
node1.Attributes["ValueList"].Value = "Myvalue";
}
}
In this code, nodelist.count is always zero :-(. Kindly help me to resolve the issue.
If you need to update first two values:
var slot = xml.Descendants("Slot")
.Where(n => n.Attribute("name").Value == "sourcePatientInfo")
.FirstOrDefault();
if(slot == null)
{
throw new WhateverAppropriateHereEcxeption();
}
var values = slot.Descendants("Value").ToList();
if(values.Count < 2)
{
throw new WhateverAppropriateHereEcxeption();
}
values[0].Value = "PID-3|MyPID" // updating the first value
values[1].Value = "PID-5|MyName" // updating the second value
if you have to search by value you can do:
bool UpdateValue(XElement slot, string oldValue, string newValue)
{
var elem = slot.Descendants("Slot")
.Where(n => n.Name == "Value" && n.Value == oldValue)
.FirstOrDefault();
if(elem != null)
{
elem = newValue;
return true;
}
return false;
}
and inside some function
var slot = xml.Descendants("Slot")
.Where(n => n.Attribute("name").Value == "sourcePatientInfo")
.FirstOrDefault();
if(slot == null)
{
throw new WhateverAppropriateHereEcxeption();
}
UpdateValue(slot, "PID-3|pid1^^^&1.2.3&ISO", "PID-3|MyPID");
UpdateValue(slot, "PID-5|Doe^John^^^", "PID-5|MyName");
UPD when you call xml.Descendants("Slot") xml look only for elements in default namespace. I use an extension method as a quick workaround to avoid that:
public static IEnumerable<XElement> NsDescendants(this XContainer e, string elementName)
{
return e.Descendants().Where(d => d.Name.LocalName == elementName);
}
Finally my problem is solved with the following code.
XmlDocument xmlDocSOR = new XmlDocument();
XmlDocSOR.Load("filename.xml");
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDocSOR.NameTable);
namespaceManager.AddNamespace("rs", "urn:oasis:names:tc:ebxml-regrep:registry:xsd:2.1");
namespaceManager.AddNamespace("ns", "urn:oasis:names:tc:ebxml-regrep:rim:xsd:2.1");
var query = "/rs:SubmitObjectsRequest/ns:LeafRegistryObjectList/ns:ExtrinsicObject/ns:Slot";
XmlNodeList nodeList = xmlDocSOR.SelectNodes(query, namespaceManager);
foreach (XmlNode plainnode in nodeList)
{
if (plainnode.Attributes["name"].Value == "sourcePatientId")
{
XmlNode childnode = plainnode.LastChild;
XmlElement ee1 = (XmlElement)childnode.FirstChild;
ee1.InnerText = sPatientID;
}
}
xmlDocSOR.Save("filename.xml");

Child Nodes in XML File

I am trying to get the child nodes in my xml file and print it out..however i cannot. It is just printing one element instead of all. please help.
XDocument doc = XDocument.Load(GlobalClass.GlobalUrl);
XElement xelement = XElement.Load(GlobalClass.GlobalUrl);
var query = from nm in xelement.Elements("EmployeeFinance")
where (int)nm.Element("EmpPersonal_Id") == empID
select new AllowancePaid
{
Income_ID = (decimal)nm.Element("Allowance-Grade")
.Element("Amount")
};
var x = query.ToList();
foreach (var ele in x) {
Debug.WriteLine(ele.Income_ID);
}
My XML File
<EmployeeFinance>
<EmployeeEmploy_Id>4891</EmployeeEmploy_Id>
<EmpPersonal_Id>28407</EmpPersonal_Id>
<Employee_Number>11715</Employee_Number>
<Allowance-Grade>
<Amount BenListID="32">6000.00</Amount>
<Amount BenListID="59">100000.00</Amount>
</Allowance-Grade>
</EmployeeFinance>
Adding the elements
var result = from element in doc.Descendants("EmployeeFinance")
where int.Parse(element.Element("EmpPersonal_Id").Value) == tt.Employee_Personal_InfoEmp_id
select element;
foreach (var ele in result)
{
ele.Element("Allowance-Grade")
.Add(new XElement("Amount", new XAttribute("BenListID", tt.ID), tt.Amount));
doc.Save(GlobalClass.GlobalUrl);
}
problem is you selecting one amount node .Element("Amount") but there are many nodes. Change it to .Elements("Amount") and need to change few places. I haven't tested but logic should something like below will work
`
var query = from nm in xelement.Elements("EmployeeFinance")
where (int)nm.Element("EmpPersonal_Id") == empID
select new AllowancePaid
{
AllowanceList = nm.Element("Allowance-Grade").Elements("Amount").Select( a=>(decimal)a.Value).ToList()
};
foreach (var ele in query) {
foreach (var a in ele.AllowanceList) {
Debug.WriteLine(a);
}
}
XDocument xelement = XDocument.Parse(xml);
var query = from nm in xelement.Elements("EmployeeFinance")
where (int)nm.Element("EmpPersonal_Id") == 28407
select new
{
Amounts = nm.Element("Allowance-Grade")
.Elements("Amount")
.Select(
row =>
new {
id = row.Value
}
).ToList()
};
var x = query.ToList();
foreach (var ele in x)
{
foreach(var a in ele.Amounts)
Console.WriteLine(a);
}

Select number of data based of a specific node

I have the following piece of code
XmlDocument docu = new XmlDocument();
docu.Load(file);
XmlNodeList lst = docu.GetElementsByTagName("name");
foreach (XmlNode n in lst)
{
string text = n.InnerText;
var types = doc.Element("program").Element("program-function").Element("function").Descendants("type").Where(x => x.Value == text).Select(c => c.Value).ToArray();
}
my xml is as follows
<program>
<program-function>
<function>
<name>add</name>
<return-type>double</return-type>
<params>
<type>double</type>
<type-value>a</type-value>
<type>double</type>
<type-value>b</type-value>
<type>string</type>
<type-value>c</type-value>
</params>
<body> return a + b + c; </body>
</function>
<function>
<name>test</name>
<return-type>int</return-type>
<params>
<type>double</type>
<type-value>a</type-value>
<type>double</type>
<type-value>b</type-value>
</params>
<body> return a + b; </body>
</function>
</program-function>
</program>
i need to be able to get the number of <type> for each <name>
result for add should be 3 = types.count() = 3
result for test should be 2 = types.count() = 2
any advice?
EDIT : If i want to retrieve each value inside types? ie. add should contain a,b,c and test should contain a,b. Would love to store it in an array for easier retrieval
How about using Linq to Xml
var xDoc = XDocument.Parse(xml);
var functions = xDoc.Descendants("function")
.Select(f => new
{
Name = f.Element("name").Value,
Types = f.Descendants("type").Select(t=>t.Value).ToList(),
//Types = f.Descendants("type").Count()
TypeValues = f.Descendants("type-value").Select(t=>t.Value).ToList()
})
.ToList();
Try this:
XDocument doc = XDocument.Load(your file);
var vals = doc.Element("program").Element("program-function").Elements("function");
var result = vals.Select(i =>
new { name = i.Element("name"),
count = i.Elements("type").Count() }

Categories