How do I select a specific xmlnode and change it values - c#

My xml looks like this....
<?xml version="1.0" encoding="utf-8"?>
<messwerte>
<messwert>
<tag>1</tag>
<niederschlag>46</niederschlag>
<temperatur>7,6</temperatur>
<druck>4,6</druck>
</messwert>
......
</messwerte>
Now, I wanna give a a specific day where I want to change "niederschlag" "temperatur" and "druck" and I tried this:
public static void WriteXML(int day, double[] mess, string path)
{
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlElement nieder = doc.SelectSingleNode("/messwerte/messwert" + Convert.ToString(day) + "/niederschlag") as XmlElement;
if (nieder != null)
{
nieder.InnerText = Convert.ToString(mess[0]);
}
}
And it wont work.
And I know it's baaaad and super basic but i cant get it to work.......

I would suggest the reason it won't work for you, is you're trying to do 2 different things with one xpath string.
First you have to find the messwert element with a tag element that has an InnerText value matching the day value you're passing in.
Once you've identified the right element you want to change the InnerText of the niederschlag element.
Even though writing this out makes it seem quite complicated, leveraging a LINQ query can simplify it tremendously:
public static void WriteXML(int day, double[] mess, string path)
{
XmlDocument doc = new XmlDocument();
doc.Load(path);
var nieder = (from XmlElement element in doc.GetElementsByTagName("messwert")
where element.SelectSingleNode("tag").InnerText == day.ToString()
select element).First().SelectSingleNode("niederschlag");
if (nieder != null)
{
nieder.InnerText = mess[0].ToString();
}
doc.Save(path);
}
This code assumes your data is strongly controlled and that you'll never be looking for a day that isn't there.
If this isn't the case you'll have to assign the query including the First() method to a temporary variable, and check if it's null.
Something like this should work:
public static void WriteXML(int day, double[] mess, string path)
{
XmlDocument doc = new XmlDocument();
doc.Load(path);
var messwert = (from XmlElement element in doc.GetElementsByTagName("messwert")
where element.SelectSingleNode("tag").InnerText == day.ToString()
select element).FirstOrDefault();
if(messwert == null)
{
throw new ArgumentException($"The day value, doesn't exist. the value passed is {day}");
}
var nieder = messwert.SelectSingleNode("niederschlag");
if (nieder != null)
{
nieder.InnerText = mess[0].ToString();
}
doc.Save(path);
}

Related

Get Value by Name of the same Element in XML using C#

I'm having a following XML, in that I'm having two Nodes namely "param" but the Names are different. I need to get the value by Name from the node param. Kindly look at the code
void Main()
{
string _commentXml = string.Format("<root>{0}{1}</root>"
, "<param name=\"Super\">Parameter One</param>"
, "<param name=\"Supreme\">Parameter Two</param>");
XmlDocument _comment = new XmlDocument();
_comment.LoadXml(_commentXml);
XElement element = XElement.Load(_comment.DocumentElement.CreateNavigator().ReadSubtree());
TryGetElementValue(element, "param").Dump();
}
public string TryGetElementValue(XElement parentEl, string elementName, string defaultValue = null)
{
var foundEl = parentEl.Element(elementName);
if (foundEl != null)
{
var xyz = foundEl.Elements("param");
if (xyz != null)
{
return xyz.First(x => x.Attribute("name").Value == "Super").Value;
}
}
return defaultValue;
}
I can't able to get the value of param with name=Super
I refereed one of the stack-overflow question which is opt for this requirement but I can't.
Referred: XDocument get XML element by the value of its name attribute
Kindly assist me.
Why all this mess?
XDocument has a Descendants method and with linq it's easy:
var xdoc = XDocument.Parse(_commentXml);
var xel = xdoc.Descendants("param")
.Where(xElement => xElement.Attribute("name")?.Value == "Super");
You can also user XPath.
var element = doc.XPathSelectElement("/path/to/element/I/want");
In your case it would be something like this:
var element = doc.XPathSelectElement("/root/param[#name="Super"]");
Check here for more info:
https://stackoverflow.com/a/11224645/1582065

Getting innertext of XML node

When i try M Adeel Khalid kode i get nothing, and trying others i get errors. i miss something, but i cant se it. My code look like this. but i get an error on Descendants, Saying "xmlDocument does not contain a definition for descendants" As you can probably see, I'm pretty new to this, so bear with me.
protected void btnRetVare_Click(object sender, EventArgs e)
{
fldRetVare.Visible = true;
try
{
functions func = new functions();
bool exists = func.checForMatch(txtRetVare.Text);
string myNumber = txtRetVare.Text;
if (Page.IsValid)
{
if (!exists)
{
txtRetVare.Text= "Varenummer findes ikke";
}
else
{
XmlDocument xmldoc = new XmlDocument();
//xmldoc.Load(Server.MapPath(map));
xmldoc.LoadXml(Server.MapPath(map));
//var Varenummer2055component = xmldoc.SelectNodes("s/Reservedele/Component[Varenummer/text()='"+txtRetVare+"']/Remarks");
//if (Varenummer2055component.Count == 1)
//{
// var remarks = Varenummer2055component[0].InnerText;
// txtRetBemærkninger.Text = remarks.ToString();
//}
string remarks = (from xml2 in xmldoc.Descendants("Component")
where xml2.Element("Varenummer").Value == txtRetVare.Text
select xml2.Element("Remarks")).FirstOrDefault().Value;
txtRetBemærkninger.Text = remarks;
}
}
You can get it this way.
XDocument xdoc = XDocument.Load(XmlPath);
string remarks = (from xml2 in xdoc.Descendants("Component")
where xml2.Element("Varenummer").Value == "2055"
select xml2.Element("Remarks")).FirstOrDefault().Value;
I've tested this code.
Hope it helps.
Use XPath to select the correct node:
XmlDocument xml = new XmlDocument();
xml.LoadXml(#"
<Reservedele>
<Component>
<Type>Elektronik</Type>
<Art>Wheel</Art>
<Remarks>erter</Remarks>
<Varenummer>2055</Varenummer>
<OprettetAf>jg</OprettetAf>
<Date>26. januar 2017</Date>
</Component>
<Component>
<Type>Forbrugsvarer</Type>
<Art>Bulb</Art>
<Remarks>dfdh</Remarks>
<Varenummer>2055074</Varenummer>
<OprettetAf>jg</OprettetAf>
<Date>27. januar 2017</Date>
</Component>
</Reservedele>");
var Varenummer2055component = xml.SelectNodes("s/Reservedele/Component[Varenummer/text()='2055']/Remarks");
if (Varenummer2055component.Count == 1)
{
var remarks = Varenummer2055component[0].InnerText;
}
I think extension method First of LINQ to XML will be simple enough and fill requirements of your questions.
var document = XDocument.Load(pathTopXmlFile);
var remark =
document.Descendants("Component")
.First(component => component.Element("Varenummer").Value.Equals("2055"))
.Element("Remarks")
.Value;
First method will throw exception if xml doesn't contain element with Varenummer = 2055
In case where there is possibility that given number doesn't exists in the xml file you can use FirstOrDefault extension method and add checking for null
var document = XDocument.Load(pathTopXmlFile);
var component =
document.Descendants("Component")
.FirstOrDefault(comp => comp.Element("Varenummer").Value.Equals("2055"));
var remark = component != null ? component.Element("Remarks").Value : null;
For saving new value you can use same "approach" and after setting new value save it to the same file
var document = XDocument.Load(pathTopXmlFile);
var component =
document.Descendants("Component")
.FirstOrDefault(comp => comp.Element("Varenummer").Value.Equals("2055"));
component.Element("Remarks").Value = newValueFromTextBox;
document.Save(pathTopXmlFile);
One more approach, which will be overkill in your particular case, but can be useful if you use other values of xml. This approach is serialization.
You can create class which represent data of your xml file and then just use serialization for loading and saving data to the file. Examples of XML Serialization

XML - Write a single node

I got this function for simply get the inner text of my xml:
XmlDocument document = new XmlDocument();
document.Load("game.xml");
string content = document.SelectSingleNode("Game/Client-Version").InnerText;
(this is the xml file (due to complications with stackoverflow posted on pastebin)): http://pastebin.com/EEeFAJpC
And now I am exactly looking for the function above, just to write. Like
document.WriteSingleNode("Game/Client-Version", "texttowrite");
I did not find anything helping me out.
This should work
XmlElement x = document.SelectSingleNode("Game/Client-Version") as XmlElement;
x.InnerText = "texttowrite";
Create your own extension method:
public void WriteSingleNode(this XmlDocument document, string NodeName, string InnerText)
{
// Create a new element node.
XmlNode newElem = document.CreateNode("element", "pages", "");
newElem.InnerText = InnerText;
Console.WriteLine("Add the new element to the document...");
document.DocumentElement.AppendChild(newElem);
Console.WriteLine("Display the modified XML document...");
Console.WriteLine(document.OuterXml);
}

Locating XML node by child node value inside of it, and changing another value

Three part question.
Is it possible to locate a specific XML node by a child inside of it to retrieve other children of the parent? Example:
<House>
<Kitchen>
<Appliance>
<Name>Refrigerator</Name>
<Brand>Maytag</Brand>
<Model>F2039-39</Model>
</Appliance>
<Appliance>
<Name>Toaster</Name>
<Brand>Black and Decker</Brand>
<Model>B8d-k30</Model>
</Appliance>
</Kitchen>
</House>
So for this, I would like to locate the appropriate Appliance node by searching for "Refrigerator" or "Toaster", and retrieve the brand from it.
The second part of this question is this: Is this a stupid way to do it? Would using an attribute in the Appliance tag make this a lot easier? If so, how would I locate it that way?
As for the third part, once I locate the Appliance, how would I go about changing say, the Model, of that particular appliance?
Using XLinq, you can perform this query fairly naturally:
// Given:
// var xdoc = XDocument.Load(...);
// string applianceName = "Toaster";
// Find the appliance node who has a sub-element <Name> matching the appliance
var app = xdoc.Root
.Descendants("Appliance")
.SingleOrDefault(e => (string)e.Element("Name") == applianceName);
// If we've found one and it matches, make a change
if (app != null)
{
if (((string)app.Element("Model")).StartsWith("B8d-k30"))
{
app.Element("Model").Value = "B8d-k30 Mark II";
}
}
xdoc.Save(#"output.xml"); // save changes back to the document
Well if you are using XmlDocument
foreach(XmlNode applianceNode in
myDocument.DocumentElement.SelectNodes("Kitchen/Applicance[Name='Refrigerator']")
{
XmlNode modelNode = applicianceNode.SelectSingleNode("Model").InnerText = SomeOtherValue;
}
if you made the name tag an attribute (applicanceName) it would make little difference to this.
foreach(XmlNode applianceNode in
myDocument.DocumentElement.SelectNodes("Kitchen/Applicance[#applianceName='Refrigerator']")
{
// ...
}
string xml = #"<House>
<Kitchen>
<Appliance>
<Name>Refrigerator</Name>
<Brand>Maytag</Brand>
<Model>F2039-39</Model>
</Appliance>
<Appliance>
<Name>Toaster</Name>
<Brand>Black and Decker</Brand>
<Model>B8d-k30</Model>
</Appliance>
</Kitchen>
</House>";
XDocument xdoc = XDocument.Parse(xml);
string newModel = "B8d-k45";
var matchingElement = (from appliance in xdoc.Descendants("Appliance")
where appliance.Element("Name").Value == "Toaster"
select appliance).FirstOrDefault();
if (matchingElement != null)
{
matchingElement.Element("Model").Value = newModel;
}
Console.WriteLine(xdoc.ToString());
Necromancing.
Yes, it's even simpler with XPath, and works completely without Linq:
Just use .. to get to the parent node (on the second thought, Linq will be easier when using "ordinalignorecase")
public static void CreateNewHouse()
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.XmlResolver = null;
doc.Load(#"d:\House.xml");
foreach (System.Xml.XmlNode modelNode in doc.DocumentElement
.SelectNodes("/House/Kitchen/Appliance/Name[text()='Refrigerator']/../Model"))
{
modelNode.InnerText = "A New Value";
}
doc.Save(#"d:\MyHouse.xml");
}
MyHouse.xml:
<House>
<Kitchen>
<Appliance>
<Name>Refrigerator</Name>
<Brand>Maytag</Brand>
<Model>A New Value</Model>
</Appliance>
<Appliance>
<Name>Toaster</Name>
<Brand>Black and Decker</Brand>
<Model>B8d-k30</Model>
</Appliance>
</Kitchen>
</House>
If you need it case-insensitive, replace text() with this:
translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')
(for ASCII/english-only) and of course change "Refrigerator" to lowercase ("refrigerator")
If the XML-document has a default-namespace, you need to supply it in Select*Node, e.g.
xnImageTag.SelectSingleNode("./dft:Source", nsmgr);
where
System.Xml.XmlNamespaceManager nsmgr = GetReportNamespaceManager(doc);
public static System.Xml.XmlNamespaceManager GetReportNamespaceManager(System.Xml.XmlDocument doc)
{
if (doc == null)
throw new ArgumentNullException("doc");
System.Xml.XmlNamespaceManager nsmgr = new System.Xml.XmlNamespaceManager(doc.NameTable);
// <Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
if (doc.DocumentElement != null)
{
string strNamespace = doc.DocumentElement.NamespaceURI;
System.Console.WriteLine(strNamespace);
nsmgr.AddNamespace("dft", strNamespace);
return nsmgr;
}
nsmgr.AddNamespace("dft", "http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition");
// nsmgr.AddNamespace("dft", "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition");
return nsmgr;
} // End Function GetReportNamespaceManager

Update or inserting a node in an XML doc

I am a beginner to XML and XPath in C#. Here is an example of my XML doc:
<root>
<folder1>
...
<folderN>
...
<nodeMustExist>...
<nodeToBeUpdated>some value</nodeToBeUpdated>
....
</root>
What I need is to update the value of nodeToBeUdpated if the node exists or add this node after the nodeMustExist if nodeToBeUpdated is not there. The prototype of the function is something like this:
void UpdateNode(
xmlDocument xml,
string nodeMustExist,
string nodeToBeUpdte,
string newVal
)
{
/*
search for XMLNode with name = nodeToBeUpdate in xml
to XmlNodeToBeUpdated (XmlNode type?)
if (xmlNodeToBeUpdated != null)
{
xmlNodeToBeUpdated.value(?) = newVal;
}
else
{
search for nodeMustExist in xml to xmlNodeMustExist obj
if ( xmlNodeMustExist != null )
{
add xmlNodeToBeUpdated as next node
xmlNodeToBeUpdte.value = newVal;
}
}
*/
}
Maybe there are other better and simplified way to do this. Any advice?
By the way, if nodeToBeUpdated appears more than once in other places, I just want to update the first one.
This is to update all nodes in folder:
public void UpdateNodes(XmlDocument doc, string newVal)
{
XmlNodeList folderNodes = doc.SelectNodes("folder");
if (folderNodes.Count > 0)
foreach (XmlNode folderNode in folderNodes)
{
XmlNode updateNode = folderNode.SelectSingleNode("nodeToBeUpdated");
XmlNode mustExistNode = folderNode.SelectSingleNode("nodeMustExist"); ;
if (updateNode != null)
{
updateNode.InnerText = newVal;
}
else if (mustExistNode != null)
{
XmlNode node = folderNode.OwnerDocument.CreateNode(XmlNodeType.Element, "nodeToBeUpdated", null);
node.InnerText = newVal;
folderNode.AppendChild(node);
}
}
}
If you want to update a particular node, you cannot pass string nodeToBeUpdte, but you will have to pass the XmlNode of the XmlDocument.
I have omitted the passing of node names in the function since nodes names are unlikely to change and can be hardcoded. However, you can pass these to the functions and use the strings instead of hardcoded node names.
The XPath expression that selects all instances of <nodeToBeUpdated> would be this:
/root/folder[nodeMustExist]/nodeToBeUpdated
or, in a more generic form:
/root/folder[*[name() = 'nodeMustExist']]/*[name() = 'nodeToBeUpdated']
suitable for:
void UpdateNode(xmlDocument xml,
string nodeMustExist,
string nodeToBeUpdte,
string newVal)
{
string xPath = "/root/folder[*[name() = '{0}']]/*[name() = '{1}']";
xPath = String.Format(xPath, nodeMustExist, nodeToBeUpdte);
foreach (XmlNode n in xml.SelectNodes(xPath))
{
n.Value = newVal;
}
}
Have a look at the SelectSingleNode method MSDN Doc
your xpath wants to be something like "//YourNodeNameHere" ;
once you have found that node you can then traverse back up the tree to get to the 'nodeMustExist' node:
XmlNode nodeMustExistNode = yourNode.Parent["nodeMustExist];

Categories

Resources