Overwrite specific XML node - c#

I have a XML file of the following format:
<Alarms>
<Alarm>
<Id>1</Id>
<Severity>Warning</Severity>
<Comments></Comments>
</Alarm>
<Alarm>
<Id>2</Id>
<Severity>Error</Severity>
<Comments>Restart the machine</Comments>
</Alarm>
...
My program has a GUI which gives the user the ability to edit the Comments of an alarm. I am trying to come up with the best solution for the actions to take when a user is done editing and wants to save the changes. The XML file isn't extremely large (it does not warrant a database) but large enough that I do not want to overwrite the entire thing every time a change is made to a single alarm. Is it possible to target only a specific node and edit the Comments attribute without then having to re-write everything?
I'm looking for a XML-specific solution... I want to avoid regular flat-file methods that involve going to a specific line in a file and then editing that line. Perhaps something exists for XML files that I'm not privy to. I'm currently working with a .NET 2 project but will soon be upgrading to 4.5, so any solution works for me.

You can load up the xml in XmlDocument class. Navigate with an XPath query to the Comments node you want to edit and change the value. When you are done, just save the document to the same file name or a different one.
Here is an example using a Console Application.
// The Id of the Alarm to edit
int idToEdit = 2;
// The new comment for the Alarm
string newCommentValue = "Here is a new comment";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode commentsElement = doc.SelectSingleNode(String.Format("Alarms/Alarm[Id = '{0}']/Comments", idToEdit));
commentsElement.InnerText = newCommentValue;
doc.Save(Console.Out);
Here is a working fiddle: https://dotnetfiddle.net/eQROet

Related

Recreating XML but passing variable into the data

I am trying to Create an XML with the below format but keep having issues. I am new to both c# and XML and hit a brick wall on this one. The reference XML i have is this
<workspace name="Remote Apps" xmlns="http://schemas.microsoft.com/ts/2008/09/tswcx" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<defaultFeed url="CHANGEME" />
</workspace>
I need to be able to recreate this file with the adjustment of the url Value. I have tried loading this XML on its own and the alter the value and also tried to recreate from scratch. Both i have hit a blank wall.
Last attempt to do this was
XDocument doc = XDocument.Load("cloud.xml");
var element = doc.Elements("defaultFeed")
.Single(x => x.Attribute("url").Value == "ChangeMe");
element.SetAttributeValue("url", fullurl);
doc.Save(CloudFileLocation);
If someone would be kind enough to help me out with this either writing from scratch or altering the code from a local file i would appreciate it. (Create from scratch would be preferred)
There are three probems with your code:
doc.Elements("defaultFeed") searches for an element named defaultFeed as child of the document. Your element is however a child of the root node workspace.
Solution: change to
doc.Root.Elements("defaultFeed")
This attribute xmlns="http://schemas.microsoft.com/ts/2008/09/tswcx" says that all elements where the namespace is not explicitely specified, are in the namespace http://schemas.microsoft.com/ts/2008/09/tswcx. Elements("defaultFeed") however searches for elements without any namespace.
Solution: change to
XNamespace tswcx = "http://schemas.microsoft.com/ts/2008/09/tswcx";
doc.Root.Elements(tswcx + "defaultFeed")
The third one is a simple typo: You should compare to "CHANGEME", not to "ChangeMe"

Replace Self Closing Node with Paired Empty nodes in XML in SQL Server

I am writing an SSIS Package where we bring in an existing XML and then Add couple nodes to it and then spit it back out. It has to be exactly the same way it goes in and comes back out, except for the 3 new columns. I use a combination of SQL Server and C# Script in SSIS to do this.
However this is the issue:
The original XML document has these nodes for example:
<Base_Entry>
<Customer></Customer>
<Profit></Profit>
...
When I put the data into the database in SQL Server and then do some parsing for the other nodes and then do try to output the XML back out it comes out looking like this for the 2 nodes.
<Base_Entry>
<Customer />
<Profit />
Now I know that semantically the self closing node is the same as the one on top. It's for blanks. However, our vendor can't work with it and is requesting it to NOT be a self closing node. He wants it just like the original.
However I can't figure it out no matter what I do in SQL Server. Blank nodes come out with a self closing node.
I tried changing it in C# using a script task for SSIS trying to do a
XMLOutput.InnerXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
Dts.Variables["XMLOutput"].Value.ToString().Replace("<Customer />", "<Customer></Customer>");
The XMLOutput.InnerXml has the entire XML content and I am trying to just replace the words in there manually by trying to do a Replace.
If anyone can help me out how to do it with just SQL Server first, that would be appreciated the most. IF not, is there a way to just replace it in C#?

How do you cycle through nodes in XML file [duplicate]

This question already has answers here:
Reading Xml with XmlReader in C#
(7 answers)
Closed 6 years ago.
I have an XML file and the structure is roughly like this:
<ScenarioList>
<Scenario>
... various things
</Scenario>
</ScenarioList>
This is being read into a WPF dialog box. Every time the user selects 'Next' I want to read the next Scenario data into the various fields. Obviously, the same thing goes for the user clicking on the 'Last' button, too.
But the question is: how do i just read in the information from the selected Scenario node?
Let me to try and clarify the question since it appears that it is 'overly broad':
I am familiar with loading and reading entire XML files. I just want to selectively read specific nodes. For example: How can I read just the data for the first Scenario node? Then, depending on user input, read just the data for the second Scenario node? Then, depending on user input, read just the data for the first scenario node? Or just the third scenario node?
In essence, I'm asking since XML doesn't have an 'index' how do I specify which instance of a node to read?
First you need to load the xml into an XmlDocument, then you can select the specific nodes and iterate over them, like this:
XmlDocument xml = new XmlDocument();
xml.LoadXml("your xml");
XmlNodeList list = xml.SelectNodes("/ScenarioList/Scenario");
foreach (XmlNode xn in list)...
You can traverse to node by using JXPath.
If you need to evaluate multiple paths relative to a certain node in the object graph, you might want to create a relative JXPathContext.
First, obtain the pointer for the location that is supposed to be the root the relative context. Then obtain the relative context by calling
JXPathContext context = JXPathContext.newContext(bean);
Pointer addressPtr = context.getPointer("/employees[1]/addresses[2]");
JXPathContext relativeContext =
context.getRelativeContext(addressPtr);
// Evaluate relative path
String zipCode = (String)relativeContext.getValue("zipCode");
// Evaluate absolute path
String name = (String)relativeContext.getValue("/employees[1]/name");
// Use the parent axis to locate the employee for the current address
Double salary = (Double)relativeContext.getValue("../salary");
you can refer in more detail from here

How do I set a xml Node to contain a string's content?

Let's say I have one string called data.
How do i put a string's text inside one of it's nodes? How do i choose which one contains the string?
unlike the other questions i saw, my xml will contain many tags. so i dont want so set it to contain one thing, but to add it to the others.
You can use the following to add a text node to your XML file with the data you have:
// Open the XML
XmlDocument doc = new XmlDocument();
doc.LoadXml("<somedata><moredata>sometext</moredata></somedata>");
// Create the new node, set to text and insert the data
XmlNode newElem = doc.CreateNode("text", "yournodename", "");
newElem.InnerText = data;
// Write the new node and append
XmlElement root = doc.DocumentElement;
root.AppendChild(newElem);
If you need something more specific, please provide some more information regarding the node name or the XML Document etc.
UPDATE: As mentioned in the comments, the above code will modify but not save the modifications to the XML. To do so, load the XML document using doc.Load("pathtoyourxml.xml") instead of using doc.LoadXml() and save it to the same path using doc.Save("pathtoyourxml.xml") after doing whatever you are doing with it.

How to write to xml elements temporarily and clear the elements value that are written as soon as method is executed?

I have one xml template that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<EmailTemplate>
<subject></subject>
<displayName></displayName>
<Message1>
</Message1>
<Copyright></Copyright>
</EmailTemplate>
I am using LINQ to write values to the elements when the method is executing. after i write the values i use xslt transformation and get the html output in the same method. Everything works fine. but what i want is i want this xml to look like above. I mean the elements shouldn't contain any value after the method is executed successfully. At the moment as soon as the method is executed the xml contains values. My code for writing to xml looks like this:
var xmlElement = XElement.Load(#"myxmlfile.xml");
var element3 = xmlElement.Elements("subject").Single();
element3.Value = subject;
var element4 = xmlElement.Elements("displayName").Single();
element4.Value = displayName;
xmlElement.Save(#"myxmlfile.xml");
Note: if i don't include the last line (xmlelement.save...) during the transformation it doesn't pickup the values. Any help and suggestion most welcome.
Remove the call to Save. Just edit the XML in memory. The Save method is used to modify the XML file on disk. If you don't want that, then don't call the Save method.
If you need a fresh copy of the XML every time you need to create HTML, then I would suggest loading the string contents of the XML file in memory so you don't have to read from the disk every time, and using a StringReader to create the XML Document.
Based on the code you've provided, it looks like you wouldn't need to load a fresh XML document every time because you would just overwrite the existing values in memory with the new ones.

Categories