I have a c# app that writes to XML using the following code:
//Write last compliant elements to state XML if allCompliant bool == true
if (allCompliant == true)
{
if (File.Exists(MainEntry.thirdPartyStateXMLPath))
{
XDocument doc = XDocument.Load(MainEntry.thirdPartyStateXMLPath);
DateTime localCurrentTime = DateTime.Now;
DateTime utcCurrentTime = DateTime.UtcNow;
XElement root = new XElement("Compliance_Status");
root.Add(new XElement("Last_Known_Compliant_UTC", utcCurrentTime));
root.Add(new XElement("Last_Known_Compliant_LocalTime", localCurrentTime.ToString()));
doc.Element("Compliance_Items").Add(root);
doc.Save(MainEntry.thirdPartyStateXMLPath);
}
}
That renders the following XML:
<?xml version="1.0" encoding="utf-8"?>
<Compliance_Items>
<Compliance_Status>
<Last_Known_Compliant_UTC>2014-04-03T23:22:31.507088Z</Last_Known_Compliant_UTC>
<Last_Known_Compliant_LocalTime>4/3/2014 4:22:31 PM</Last_Known_Compliant_LocalTime>
</Compliance_Status>
</Compliance_Items>
This code generates the *Last_Known_Compliant_UTC* and *Last_Known_Compliant_LocalTime* elements and values. On subsequent runs of the code I want it to only replace the values of the existing elements, but as written now the following is re-created each time and keeps stacking in the XML:
<Compliance_Status>
<Last_Known_Compliant_UTC>2014-04-03T23:22:31.507088Z</Last_Known_Compliant_UTC>
<Last_Known_Compliant_LocalTime>4/3/2014 4:22:31 PM</Last_Known_Compliant_LocalTime>
</Compliance_Status>
How can I achieve the desired effect?
If you want to get a specific item and modify it you can do the following:
var xmlDocument = XDocument.Load("path");
var element = xmlDocument
.Descendants("Compliance_Status")
.FirstOrDefault(x => (DateTime)x.Element("Last_Known_Compliant_UTC") == someValue)
if(element != null)
/* update the value simply using element.Value = something
and save the xml file xmlDocument.Save("path") */
If you want to replace your element with another element you can use XElement.ReplaceWith method.
Related
I used this code to export to XML file from DataTable:
dt1 = TNET2_POHeadService.TNET2_POHead_GetByPO_NoRaw(PO_No);
dt2 = TNET2_PODetailsService.TNET2_PODetails_GetByPO_NoRaw(PO_No);
ds.Tables.Add(dt1);
ds.Tables[0].TableName = "TNET2_POHead";
ds.Tables.Add(dt2);
ds.Tables[1].TableName = "TNET2_PODetails";
saveFileDialog1.ShowDialog();
ds.WriteXml(saveFileDialog1.FileName);
Everything worked fine but :
<ContractNo>P1717-198905-003(01)</ContractNo>
<KP_No xml:space="preserve"> </KP_No>
<SettlementRoute1>TENTAC SUZHOU</SettlementRoute1>
KP_No tag is blank value. I want to remove xml:space="preserve" of the XML export file. How to do that?
The XDocument class in the System.Xml.Linq namespace allows for easy manage and manipulation of XML using LINQ.
See example below to remove the xml:space="preserve" attribute from the KP_No element of the XML file:
XDocument doc = XDocument.Load("XMLFile1.xml"); // or XDocument.Load(myStream) ...
Func<XAttribute, bool> preserveAttrFunc = atr => atr.Name.LocalName == "space" && atr.Value == "preserve";
XElement kp_no = doc.Descendants("KP_No").FirstOrDefault(kp => kp.HasAttributes && kp.Attributes().Any(preserveAttrFunc));
if(kp_no != null)
kp_no.Attributes().FirstOrDefault(preserveAttrFunc).Remove();
// doc.Save(filename) ...
Note your XML doesn´t have a root element. It's good practice to have one defined in XML.
Result after the code snippet above:
<root>
<ContractNo>P1717-198905-003(01)</ContractNo>
<KP_No></KP_No>
<SettlementRoute1>TENTAC SUZHOU</SettlementRoute1>
</root>
For example, I have this xml string:
<?xml version="1.0" encoding="utf-8"?>
<data>
<text>How to get <bold>all</bold> this string's content?</text>
</data>
I want to get all these elements in an array of objects (for each object I have a class), without loosing their structure:
[1] (TextClass; where bold = false) How to get
[2] (TextClass; where bold = true) all
[3] (TextClass; where bold = false) this string's content?
All I'm getting using XmlDocument and XmlNode classes right now is InnerText Or InnerXml separately.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("example.xml");
foreach (XmlNode child in xmlDoc.DocumentElement.ChildNodes)
{
string chName = child.Name; // text
string text = child.InnerText; // How to get all this string's content?
string xml = child.InnerXml; // How to get <bold>all</bold>this string's content?
}
Is it possible?
For this kind of work I think it is easier to use the LINQ to XML.
In your example something like the following could work (depending on exactly what you want to achieve):
XDocument doc = XDocument.Parse(xml);
var textClasses = from n in doc.Descendants("text").DescendantNodes()
where n.NodeType == XmlNodeType.Text
select new { text = ((XText)n).Value, bold = n.Parent?.Name == "bold" };
And a .net fiddle so you can quickly see the result.
I have an string parameter with xml content in it. Basically the string have an XML inside.
string S = funcThatReturnsXML (parameters);
S have the next text:
<?xml version="1.0" encoding="utf-8" ?>
<tagA>
<tagB>
<tagBB>
..
.
.
</tagBB>
.
.
</tagB>
<tagC>
..
..
.
</tagC>
</tagA>
The funcThatReturnsXML (parameters) creates an XmlDocument object but the return it as a string, I cant change this function, to much stuff works with it.
Tried to create XmlDocument objetc but the SelectSingleNode return null.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
XmlNode root = xmlDoc.SelectSingleNode("tagB");
How can I delete from string S (not XML Object) specific node, for example <tagB>
EDIT: this is the XML I tested with:
<?xml version="1.0" ?>
- <Request xmlns:xsi="http://www.mysite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <info xmlns="http://www.mysite.com">
<RequestTR>54</RequestTR>
<time>2013-12-22</time>
</info>
- <Parameters xmlns="http://www.mysite.com">
<id>3</id>
<name>2</name>
</Parameters>
<title>Request</title>
</Request>
Try this:
string S = funcThatReturnsXML(parameters);
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants("tagB");
nodeToRemove.Remove();
That will remove all nodes named "tagB" from string S which contains xml.
UPDATE 1:
Sorry, i missed to include one more line:
S = doc.ToString();
My first code above removed "tagB" from doc but didnt save it back to S variable.
UPDATE 2:
I tested with following xml which contain attribute:
<tagA attribute="value">
<tagB>
<tagBB>
</tagBB>
</tagB>
<tagC></tagC>
</tagA>
and the output of Console.WriteLine(S):
<tagA attribute="value">
<tagC></tagC>
</tagA>
UPDATE 3:
Given your updated xml format, I know why my previous code didn't work for you. That was because your xml have namespace (xmlns) declared. The solution is to use LocalName when searching for the node to be removed, that will search for node name while ignoring its namespace. The follwoing example shows how to remove all "info" node:
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants().Where(o => o.Name.LocalName == "info");
nodeToRemove.Remove();
S = doc.ToString();
If you can determine the particular outer element to remove from the returned XML, you could use LINQ to XML:
var returnedXml = funcThatReturnsXML(parameters);
var xmlElementToRemove = funcThatReturnsOuterElement(returnedXml);
var xelement = XElement.Load("XmlDoc.txt");
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
For example:
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
// pretend this is the funThatReturnsXML return value
var returnedXml = "<tagB><tagBB></tagBB></tagB>";
// get the outer XML element name
var xmlElementToRemove = GetOuterXmlElement(returnedXml);
// load XML from where ever
var xelement = XElement.Load("XmlDoc.txt");
// remove the outer element and all subsequent elements
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
}
static string GetOuterXmlElement(string xml)
{
var index = xml.IndexOf('>');
return xml.Substring(1, index - 1);
}
}
Note that the above is a "greedy" removal method, if there is more than once element with the name returned via the GetOuterXmlElemet method they will all be removed. If you want a specific instance to be removed then you will require something more sophisticated.
Building on your edit:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
var nodeA = xmlDoc.SelectSingleNode("/tagA");
var nodeB = nodeA.SelectSingleNode("tagB");
nodeA.RemoveChild(nodeB);
To remove (possibly) multiple tagB nodes in unknown positions, you may try:
var bees = xmlDoc.SelectNodes("//tagB");
foreach (XmlNode bee in bees) {
var parent = bee.ParentNode;
parent.RemoveChild(bee);
}
everyone!
I have an XML file and need to change the value of a node, specifically the indicated line. The problem i have is that as you can see, there are many nodes.
How can i change this line? This XML file could be much larger, so i am looking for a solution that would take different amounts of 'launch.file' nodes into account.
The node that will need to be set to True will be identified by the corresponding NAME tag. So if i typed in ULTII, the DISABLED node for that block will be set to True. If i typed in Catl, then the DISABLED node for that block would be changed.
<?xml version="1.0" encoding="windows-1252"?>
<SBase.Doc Type="Launch" version="1,0">
<Descr>Launch</Descr>
<Filename>run.xml</Filename>
<Disabled>False</Disabled>
<Launch.ManualLoad>False</Launch.ManualLoad>
<Launch.File>
<Name>Catl</Name>
<Disabled>False</Disabled>
<ManualLoad>False</ManualLoad>
<Path>ft\catl\catl.exe</Path>
</Launch.File>
<Launch.File>
<Disabled>False</Disabled> <!-- change to True -->
<ManualLoad>False</ManualLoad>
<Name>ULTII</Name>
<Path>F:\ULTII.exe</Path>
<NewConsole>True</NewConsole>
</Launch.File>
<Launch.File>
<Name>ECA</Name>
<Disabled>False</Disabled>
<Path>C:\ECA.exe</Path>
</Launch.File>
</SBase.Doc>
I am using Visual Studio 2012, should you need to know.
Thank you to anyone who can help me out on this, i really appreciate it.
Heres my method to do what you want
private void DisableLaunchFile(string xmlfile, string launchFileName){
XDocument doc = XDocument.Load(xmlfile);
var launchFileElement = doc.Descendants("Launch.File").Where (d => d.Element("Name").Value == lauchFileName);
launchFileElement.Elements("Disabled").First().Value = true.ToString();
doc.Save(xmlfile);
}
Use it like:
string pathToXmlFile = //assign ;
DisableLaunchFile(pathToXmlFile, "Catl");
DisableLaunchFile(pathToXmlFile, "ULTII");
This can be achieved by using LINQ to XML (see XDocument Class).
Assuming that there is the single Launch.File element with Name element with value "ULTII":
var document = XDocument.Load(...);
var ultiiElement = document
.Descendants("Launch.File")
.Single(fileElement => fileElement.Element("Name").Value == "ULTII");
ultiiElement.Element("Disabled").Value = "True"; // or true.ToString()
document.Save(...);
This method will do the trick:
public void ChangeNode(string name, string filePath)
{
XDocument xDocument;
using (var streamReader = new StreamReader(filePath))
{
xDocument = XDocument.Parse(streamReader.ReadToEnd());
}
var nodes = xDocument.Descendants("Launch.File");
foreach (var node in nodes)
{
var nameNode = node.Descendants("Name").FirstOrDefault();
if (nameNode != null && nameNode.Value == name)
{
var disabledNode = node.Descendants("Disabled").FirstOrDefault();
if (disabledNode != null)
{
disabledNode.SetValue("True");
}
}
}
using (var streamWriter = new StreamWriter(filePath))
{
xDocument.Save(streamWriter);
}
}
The name you want to pass in is the name of the node that you want to change and the path is the file path to the xml file. So you might call it like:
ChangeNode("ULTII", "C:\\output.xml");
You may need to tidy this up a bit like matching the node name invariant of case or culture but it should get you started.
I have the following code which creates an XML file with a bunch of order information. I'd like to be able to update an entry in this XML file instead of deleting everything and re-adding everything again.
I know I can do this:
xElement.Attribute(attribute).Value = value;
But that will change every attribute with the same name as attribute holds. How can I only change the value of something when the entry's Id equals "jason", for example? Would I need to Load the XML file, iterate over the entire file until it finds a match for the attribute I want to change, then change it, and then save the file again?
Any help/suggestions are greatly appreciated.
XElement xElement;
xElement = new XElement("Orders");
XElement element = new XElement(
"Order",
new XAttribute("Id", CustomId),
new XAttribute("Quantity", Quantity),
new XAttribute("PartNo", PartNo),
new XAttribute("Description", Description),
new XAttribute("Discount", Discount),
new XAttribute("Freight", Freight),
new XAttribute("UnitValue", UnitValue),
new XAttribute("LineTotal", LineTotal)
);
xElement.Add(element);
xElement.Save(PartNo + ".xml");
Here's what my XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<Orders>
<Order Id="V45Y7B458B" Quantity="2" PartNo="5VNB98" Description="New Custom Item Description" Discount="2.00" Freight="2.90" UnitValue="27.88" LineTotal="25.09" />
<Order Id="jason" Quantity="2" PartNo="jason" Description="New Custom Item Description" Discount="2.00" Freight="2.90" UnitValue="27.88" LineTotal="25.09" />
</Orders>
Something like this:
var doc = XDocument.Load("FileName.xml");
var element = doc.Descendants("Order")
.Where(arg => arg.Attribute("Id").Value == "jason")
.Single();
element.Attribute("Quantity").Value = "3";
doc.Save("FileName.xml");
First you need to search for the element that you want to update. If you find it, do the update. Just remember to save the XDocument back to the file when you're done.
XDocument doc = ...;
var jason = doc
.Descendants("Order")
.Where(order => order.Attribute("Id").Value == "jason") // find "jason"
.SingleOrDefault();
if (jason != null) // if found,
{
// update something
jason.Attribute("Quantity").SetValue(20);
}
doc.Save(...); // save if necessary
Since you created the XML file, you know the root element of the XML so you can use this code to get the particular element you want:
TaxonPath = XElement.Parse(xml as string);
txtSource.Text = FindGetElementValue(TaxonPath, TaxonPathElement.Source);
XElement FindGetElementValue(XElement tree,String elementname)
{
return tree.Descendants(elementName).FirstOrDefault();
}
With this, you can get the element, check its value, and change it as you desire.