I need to convert the URI addres to XML notation.
for example
/Test1/Test2/Test3/
to
<Modul>
<Test1/><Test2/><Test3/>
</Modul>
Here is my code:
private static XmlNode NodeRecurs(XmlNode node, string nodeName)
{
string[] array = nodeName.Split('/');
var xdoc = new XmlDocument();
var name = nodeName.Remove(0, array[0].Length + 1);
XmlNode xmlNode = xdoc.CreateNode(XmlNodeType.Element, array[0], string.Empty);
node.AppendChild(xmlNode);
if (array.Count() != 0)
{
NodeRecurs(node, name);
}
return node;
}
When the NodeRecurs calls itself it is InvalidArgument exeption. It says the it is wrong context for the current node.
To append nodes to a document, they need to be created by the same document.
You are creating a new XmlDocument every time you call the function - create one outside of the function and pass it in as a parameter.
Related
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);
}
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);
}
I have a simple XML doc with no Namespace
Here is the code I wrote in C# to search for a particular element based on Name.
public XmlElement SearchXML(string name)
{
XmlDocument xDoc = new XmlDocument();
string filePath = ConfigurationManager.AppSettings["path"];
xDoc.Load(filePath);
string xQryStr = "//NewPatient[Name='" + name + "']";
xDoc.SelectNodes(xQryStr);
XmlElement xmlEle = xDoc.DocumentElement;
return xmlEle;
}
The XML document is as follows
When I try to call the method SearchXML and pass Dennis as the arguement, instead of returning the xml element containing only the specific elements, it returns the entire document.
Where am I possibly erring?
Any help appreciated.
xDoc.SelectNodes(xQryStr) does not mutate the original doc. You need to store the return value of this method call and return that instead.
ATM you're simply returning the root element of the original doc (i.e. the entire tree)
EDIT
In answer to your comment, you could fish the first matched XmlElement as follows:
xDoc.SelectNodes(xQryStr).OfType<XmlElement>().FirstOrDefault()
This will return either null or an XmlElement
If you want to select a list of nodes based on an XPath expression, you need to use .SelectNodes in this fashion:
public XmlElement SearchXML(string name)
{
XmlDocument xDoc = new XmlDocument();
string filePath = ConfigurationManager.AppSettings["path"];
xDoc.Load(filePath);
string xQryStr = "//NewPatient[Name='" + name + "']";
XmlNodeList listOfNodes = xDoc.SelectNodes(xQryStr);
foreach(XmlNode node in listOfNodes
{
// do something with that list of XML nodes you've selected....
// XmlElement xmlEle = node;
// return xmlEle;
}
}
The call to .SelectNodes(xpath) returns a list of matching XML nodes (see the MSDN documentation on XmlDocument.SelectNodes) - once you have that list, you can iterate over the matched nodes and do something with them....
Or if you're expecting only a single XML node to match your XPath expression, you can use .SelectSingleNode, too:
string xQryStr = "//NewPatient[Name='" + name + "']";
XmlNode matchedNode = xDoc.SelectSingleNode(xQryStr);
if(matchedNode != null)
{
// do something with that list of XML nodes you've selected....
return matchedNode;
}
can you change
string xQryStr = "//NewPatient[Name='" + name + "']";
to
xQryStr = "/NewPatient[Name='" + name + "']";
see the below link
http://www.csharp-examples.net/xml-nodes-by-name/
I am querying a xml document using the XPathSelectElement method.
if the node does not exist I would like to insert a node with that path in the same document. The parent nodes should also be created if they do not exist. Is there an easy way to do this without looping through the parents checking if they exist? (Add a new node using XPath)
No, there is not... this is no different than if you were looking for a Directory on a File System, and had to ensure that all of the parent directories were there to.
Example:
if (Directory.Exists(#":c:\test1\test2\blah blah\blah blah2")) ...
It's true that the Directory.CreateDirectory method will create all parents that need to be there to have the child show up, but there is no equivalent in XML (using .NET classes, including LINQ-to-XML).
You'll have to loop through each one manually. I suggest you make a helper method called "EnsureNodeExists" that does that for you :)
static private XmlNode makeXPath(XmlDocument doc, string xpath)
{
return makeXPath(doc, doc as XmlNode, xpath);
}
static private XmlNode makeXPath(XmlDocument doc, XmlNode parent, string xpath)
{
// grab the next node name in the xpath; or return parent if empty
string[] partsOfXPath = xpath.Trim('/').Split('/');
string nextNodeInXPath = partsOfXPath.First();
if (string.IsNullOrEmpty(nextNodeInXPath))
return parent;
// get or create the node from the name
XmlNode node = parent.SelectSingleNode(nextNodeInXPath);
if (node == null)
node = parent.AppendChild(doc.CreateElement(nextNodeInXPath));
// rejoin the remainder of the array as an xpath expression and recurse
string rest = String.Join("/", partsOfXPath.Skip(1).ToArray());
return makeXPath(doc, node, rest);
}
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<feed />");
makeXPath(doc, "/feed/entry/data");
XmlElement contentElement = (XmlElement)makeXPath(doc,"/feed/entry/content");
contentElement.SetAttribute("source", "");
Console.WriteLine(doc.OuterXml);
}
This is a bit late, but for the next poor soul to come here and find no help here's the static method I ended up writing. It's not flawless, but should handle a variety of use cases. Essentially I just go from the bottom of the XPath and keep cutting off sections until I find a match, then go back down the path and create what's missing. It will fail if you use a path like /root/test//someNode if someNode doesn't exist, but for a path ../someNode//foo/bar/zoot, if ../someNode//foo exists it should work fine.
public static XElement GetOrCreateNodeAtXPath(XDocument doc, string key)
{
//you don't really need this, but it makes throwing errors easier when you're 10 levels deep in the recursion
return GetOrCreateNodeAtXPath(doc, key, key);
}
private static XElement GetOrCreateNodeAtXPath(XDocument doc, string key, string originalKey)
{
var node = doc.XPathSelectElement(key);
if (node != null)
return node;
if (!key.Contains('/')) //we've reached the root, and couldn't find anything
throw new Exception($"Could not create node at path {originalKey}, no part of path matches document");
var slashIndex = key.LastIndexOf('/');
var newKey = key.Substring(0, slashIndex);
var newNodeName = key.Substring(slashIndex + 1);
var parentNode = GetOrCreateNodeAtXPath(doc, newKey, originalKey);
var childNode = new XElement(newNodeName);
parentNode.Add(childNode);
return childNode;
}
You may wanna swap XDocument with XElement or XNode or something in the signature, I was just dealign with docs so I used docs.
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];