C# XML How to retrive innerText of field by attribute? - c#

Here is the XML sample:
<?xml version="1.0" ?>
<XMLScreen xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CX>80</CX>
<CY>24</CY>
<Formatted>true</Formatted>
<Field>
<Location position="1" left="1" top="0" length="69" />
<Attributes Base="226" Protected="false" FieldType="High" />
*SDC SCHEDULING CATEGORY UPDATE
</Field>
</XMLScreen>
I want to retrive the Inner text of each field based on its Location position.
What I have so far is:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myEm.CurrentScreenXML.GetXMLText());
XmlNodeList fields = xmlDoc.GetElementsByTagName("Field");
MessageBox.Show("Field spot: " + i + " Contains: " + fields[i].InnerText);
And I want to be able to just extract the inner text of the field by passing in a number of the location position. So if I say foo[i] I want to be able to get the innertext
*SDC SCHEDULING CATEGORY UPDATE

You should use a xpath search query :
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
int nodeId = 4;
XmlNode node = xmlDoc.SelectSingleNode(String.Format(#"//Location[#position='{0}']", nodeId));
if (node != null)
{
String field = node.ParentNode.InnerText;
}

Something like that, with XDocument instead of XmlDocument (well, if you're not in .net 3.5 or higher, we'll have a problem).
private string GetTextByLocationId(XDocument document, int id)
{
var field = document.Descendants("Field").FirstOrDefault(m => m.Element("Location").Attribute("position").Value == id.ToString());
if (field == null) return null;
return field.Value;
}
and usage
var xDocument = XDocument.Load(<pathToXmlFile or XmlReader or string or ...>);
var result = GetTextByLocationId(xDocument, 1);
EDIT
or if you want a dictionary with :key = position / value = text
private static Dictionary<int, string> ParseLocationAndText(XDocument document)
{
var fields = document.Descendants("Field");
return fields.ToDictionary(
f => Convert.ToInt32(f.Element("Location").Attribute("position").Value),
f => f.Value);
}

Try,
XElement root = XElement.Parse(myEm.CurrentScreenXML.GetXMLText());
XElement field = root.XPathSelectElement(
string.Format("Field[Location/#position='{0}']", 1));
string text = field.Value;
You will need to use the following using to use XPath with XElements.
using System.Xml.XPath;

Related

how to read XML file and change some value

Xml file :
<configuration>
<work.config>
<variable name="A" value="001" />
<variable name="B" value="002" />
<variable name="C" value="003" />
</work.config>
</configuration>
and next I write some code to read XML
XmlDocument cc = new XmlDocument();
cc.Load("wc.config");
XmlNodeList wc_value = cc.SelectNodes("configuration/work.config[#name='A']");
foreach (XmlNode wc_text in wc_value)
{
String Text2 = wc_text.InnerText;
}
but Text2 is null; why, and how do I have it not null?
And next I want to change name="B" value="999", how do I do that?
Your variable elements dont not have text, that's the reason you are getting null.
Your variable elements have 2 attributes: name and value.
In order to get the value of the attribute:
string txt = wc_text.Attributes["name"].Value
Set value:
wc_text.Attributes["name"].Value = "foo".
Your problem begins in this line.
"configuration/work.config[#name='A']"
work.config attribute doesn't have an attribute called name. Following is what you actually required.
"configuration/work.config/variable[#name='A']"
Similarly, with the path fixed as above, the following line would still return empty string as you are reading the InnerText of the node.
String Text2 = wc_text.InnerText;
What you would need is to read the Value attribute of the node.
String Text2 = wc_text.Attributes["value"].Value;
If your intention is to Read and Update Value Attribute of a particular node, an easier approach would be to use XDocument.
// Assuming there are no duplicate names. You need to change accordingly the Queries if there are duplicates
public void UpdateKey(XDocument document,string key,string value)
{
var node = document.Descendants("variable").First(x=>(string)x.Attribute("name")==key);
node.Attribute("value").Value = value;
}
public string ReadKey(XDocument document, string key)
{
return document.Descendants("variable").First(x=>(string)x.Attribute("name")==key).Attribute("value").Value;
}
Now, you could use it as
XDocument doc = XDocument.Load(xml);
var ValueOfA = ReadKey(doc,"A");
var oldValueOfB = ReadKey(doc,"B");
UpdateKey(doc,"B","999");
var newValueOfB = ReadKey(doc,"B");
LINQ to XML
void Main()
{
XDocument xmlDoc = XDocument.Load(#"e:\temp\wc.config");
// read attributes
foreach (var leaf in xmlDoc.Descendants("variable"))
{
Console.WriteLine(leaf.Attributes("name").FirstOrDefault().Value);
Console.WriteLine(leaf.Attributes("value").FirstOrDefault().Value);
}
// modify value of the attribute
var attr = xmlDoc.Descendants("variable")
.Where(d => (string)d.Attribute("name").Value == "B");
attr.Attributes("value").FirstOrDefault().Value = "999";
}
You could use LINQ to XML to get or set value of any element or attribute based on condition as below:
include namespace System.Xml.Linq in your using directives to use XDocument
XDocument doc = XDocument.Load("wc.config");
foreach (XElement variableElement in doc.Descendants("variable"))
{
if (variableElement.Attribute("name").Value == "A")
{
variableElement.Attribute("value").Value = "";
}
else if (variableElement.Attribute("name").Value == "B")
{
variableElement.Attribute("value").Value = "999";
}
}
Use XPath language:
var xmlString = #"<configuration>
<work.config>
<variable name='A' value='001' />
<variable name='B' value='002' />
<variable name='C' value='003' />
</work.config>
</configuration>";
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xmlString);
var variables = xmlDocument.SelectSingleNode("//variable");
Try below code
XmlDocument doc = new XmlDocument();
doc.Load("C:\\config.xml");
XmlNodeList list= doc.SelectNodes("configuration/work.config/variable");
foreach (XmlNode item in list)
{
if (item.Attributes["name"].InnerText == "B")
{
item.Attributes["value"].InnerText = "999";
}
}

Select single node

From the following xml:
<response>
<content>
<Result xmlns="http://www.test.com/nav/webservices/types">
<Name>Test</Name>
</Result>
</content>
<status>ok</status>
</response>
I am trying to get the value of the Name element the following way but that does not work:
private static void Main()
{
var response = new XmlDocument();
response.Load("Response.xml");
var namespaceManager = new XmlNamespaceManager(response.NameTable);
namespaceManager.AddNamespace("ns", "http://www.test.com/nav/webservices/types");
Console.WriteLine(response.SelectSingleNode("/response/content/Result/Name", namespaceManager).InnerXml);
}
How can I select the Name element?
Your code would have worked just fineif the Xml had defined the namespace with a "ns:" prefix.
But in this case, the namespace is given without any prefix, which sets the default namespace for everything in the Result tag to ".../webservice/types".
To reflect this, you need to modify the Xpath, and tell the XmlDocument that the nodes you are looking for under Resultare in the webservice/types namespace. So your query will look like this:
Console.WriteLine(response.SelectSingleNode(#"/response/content/ns:Result/ns:Name", namespaceManager).InnerXml);
For getting directly the text value of a node there is a text() function, if used in the query it would look like:
/response/content/Result/Name/text()
Try this:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.InnerXml = "<response><content><Result xmlns=\"http://www.test.com/nav/webservices/types\"><Name>Test</Name></Result></content><status>ok</status>";
string elementValue = String.Empty;
if (xmlDoc != null)
{
xNode = xmlDoc.SelectSingleNode("/Result");
xNodeList = xNode.ChildNodes;
foreach (XmlNode node in xNodeList)
{
elementValue = node.InnerText;
}
}

Reading xml file on some condition

I want to use a xml file which is as below
<?xml version="1.0" encoding="utf-8" ?>
<pages>
<page name="Default.aspx">
<method name="Login_click">
<message code="0" description="this is a test description">
<client code="0000000000" description="this is a description for clent 0000000000" />
</message>
</method>
</page>
</pages>
Now I want to create a function like below
public static string GetAppMessage(string pageName, string methodName, string clientCode, string code)
{
var xmlFile = HttpContext.Current.Server.MapPath("~/App_Data/Theme.xml");
var doc = new XmlDocument();
doc.Load(xmlFile);
if (string.IsNullOrEmpty(clientCode))
{
//go to node who page name to pageName
//and read the vlue
}
else
{
//read for a particular client code
}
}
How can I do this.
Edit 1
Do I need to loop through each node or can I reach to a particular node directly and find the decedent nodes.
like below
foreach (XmlNode chldNode in doc.ChildNodes)
{
....
Instead of XmlDocument you can use XDocument and LINQ to xml:
var xmlFile = HttpContext.Current.Server.MapPath("~/App_Data/Theme.xml");
XDocument xmlDoc = XDocument.Load(xmlFile);
var xmlPage = (from page in xmlDoc.Descendants()
where page.Name.LocalName == "page"
&& page.Attribute("name").Value == pageName
select page).FirstOrDefault();
if (xmlPage != null)
{
//do what you need
}
When you use XmlDocument and you know how the XML-file will look like (I mean you know the names of the nodes where the information is inside) then you could do something like this:
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlElement root = doc["NameOfRootNode"];
if (root != null)
{
//For nodes you just need to bypass to get to another subnode:
XmlNode node = root.SelectSingleNode("nameOfAnotherNode");
//For nodes you actually want to do something with, like read text, attribute etc.
if (node != null && node.SelectSingleNode("nameOfOneMoreNode") != null)
{
var xmlElement = node["nameOfOneMoreNode"];
if (xmlElement != null)
{
//Read from the xmlElement you selected and do something with it...
}
}
//...
}
With SelectSingleNode or SelectNodes you can maneuver to a specific known node and can read the InnerText or an Attribute.
You can use XPath to get <page> element by it's name attribute, for example :
string xpath = "//page[#name='{0}']";
XmlNode page = doc.SelectSingleNode(string.Format(xpath, pageName));
//and read the vlue
Basically, above XPath look for <page> element having name attribute equals pageName parameter.

Adding info to a xml file

I have a XML file which contains about 850 XML nodes. Like this:
<NameValueItem>
<Text>Test</Text>
<Code>Test</Code>
</NameValueItem>
........ 849 more
And I want to add a new Childnode inside each and every Node. So I end up like this:
<NameValueItem>
<Text>Test</Text>
<Code>Test</Code>
<Description>TestDescription</Description>
</NameValueItem>
........ 849 more
I've tried the following:
XmlDocument doc = new XmlDocument();
doc.Load(xmlPath);
XmlNodeList nodes = doc.GetElementsByTagName("NameValueItem");
Which gives me all of the nodes, but from here am stuck(guess I need to iterate over all of the nodes and append to each and every) Any examples?
You need something along the lines of this example below. On each of your nodes, you need to create a new element to add to it. I assume you will be getting different values for the InnerText property, but I just used your example.
foreach (var rootNode in nodes)
{
XmlElement element = doc.CreateElement("Description");
element.InnerText = "TestDescription";
root.AppendChild(element);
}
You should just be able to use a foreach loop over your XmlNodeList and insert the node into each XmlNode:
foreach(XmlNode node in nodes)
{
node.AppendChild(new XmlNode()
{
Name = "Description",
Value = [value to insert]
});
}
This can also be done with XDocument using LINQ to XML as such:
XDocument doc = XDocument.Load(xmlDoc);
var updated = doc.Elements("NameValueItem").Select(n => n.Add(new XElement() { Name = "Description", Value = [newvalue]}));
doc.ReplaceWith(updated);
If you don't want to parse XML using proper classes (i.e. XDocument), you can use Regex to find a place to insert your tag and insert it:
string s = #"<NameValueItem>
<Text>Test</Text>
<Code>Test</Code>
</NameValueItem>";
string newTag = "<Description>TestDescription</Description>";
string result = Regex.Replace(s, #"(?<=</Code>)", Environment.NewLine + newTag);
but the best solution is Linq2XML (it's much better, than simple XmlDocument, that is deprecated at now).
string s = #"<root>
<NameValueItem>
<Text>Test</Text>
<Code>Test</Code>
</NameValueItem>
<NameValueItem>
<Text>Test2</Text>
<Code>Test2</Code>
</NameValueItem>
</root>";
var doc = XDocument.Load(new StringReader(s));
var elms = doc.Descendants("NameValueItem");
foreach (var element in elms)
{
element.Add(new XElement("Description", "TestDescription"));
}
var text = new StringWriter();
doc.Save(text);
Console.WriteLine(text);

How can I get the href attribute value out of an <?xml-stylesheet> node?

We are getting an XML document from a vendor that we need to perform an XSL transform on using their stylesheet so that we can convert the resulting HTML to a PDF. The actual stylesheet is referenced in an href attribute of the ?xml-stylesheet definition in the XML document. Is there any way that I can get that URL out using C#? I don't trust the vendor not to change the URL and obviously don't want to hardcode it.
The start of the XML file with the full ?xml-stylesheet element looks like this:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="http://www.fakeurl.com/StyleSheet.xsl"?>
As a processing instruction can have any contents it formally does not have any attributes. But if you know there are "pseudo" attributes, like in the case of an xml-stylesheet processing instruction, then you can of course use the value of the processing instruction to construct the markup of a single element and parse that with the XML parser:
XmlDocument doc = new XmlDocument();
doc.Load(#"file.xml");
XmlNode pi = doc.SelectSingleNode("processing-instruction('xml-stylesheet')");
if (pi != null)
{
XmlElement piEl = (XmlElement)doc.ReadNode(XmlReader.Create(new StringReader("<pi " + pi.Value + "/>")));
string href = piEl.GetAttribute("href");
Console.WriteLine(href);
}
else
{
Console.WriteLine("No pi found.");
}
Linq to xml code:
XDocument xDoc = ...;
var cssUrlQuery = from node in xDoc.Nodes()
where node.NodeType == XmlNodeType.ProcessingInstruction
select Regex.Match(((XProcessingInstruction)node).Data, "href=\"(?<url>.*?)\"").Groups["url"].Value;
or linq to objects
var cssUrls = (from XmlNode childNode in doc.ChildNodes
where childNode.NodeType == XmlNodeType.ProcessingInstruction && childNode.Name == "xml-stylesheet"
select (XmlProcessingInstruction) childNode
into procNode select Regex.Match(procNode.Data, "href=\"(?<url>.*?)\"").Groups["url"].Value).ToList();
xDoc.XPathSelectElement() will not work since it for some reasone cannot cast an XElement to XProcessingInstruction.
You can also use XPath. Given an XmlDocument loaded with your source:
XmlProcessingInstruction instruction = doc.SelectSingleNode("//processing-instruction(\"xml-stylesheet\")") as XmlProcessingInstruction;
if (instruction != null) {
Console.WriteLine(instruction.InnerText);
}
Then just parse InnerText with Regex.
To find the value using a proper XML parser you could write something like this:
using(var xr = XmlReader.Create(input))
{
while(xr.Read())
{
if(xr.NodeType == XmlNodeType.ProcessingInstruction && xr.Name == "xml-stylesheet")
{
string s = xr.Value;
int i = s.IndexOf("href=\"") + 6;
s = s.Substring(i, s.IndexOf('\"', i) - i);
Console.WriteLine(s);
break;
}
}
}
private string _GetTemplateUrl(XDocument formXmlData)
{
var infopathInstruction = (XProcessingInstruction)formXmlData.Nodes().First(node => node.NodeType == XmlNodeType.ProcessingInstruction && ((XProcessingInstruction)node).Target == "mso-infoPathSolution");
var instructionValueAsDoc = XDocument.Parse("<n " + infopathInstruction.Data + " />");
return instructionValueAsDoc.Root.Attribute("href").Value;
}
XmlProcessingInstruction stylesheet = doc.SelectSingleNode("processing-instruction('xml-stylesheet')") as XmlProcessingInstruction;

Categories

Resources