Deleting XML element nodes - c#

I want to delete an Element Node and all its Child Elements in a local xml-file by using C#.
This is my xml-file:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<cocktail name="43 Hedonism" id="14">
<name>43 Hedonism</name>
<id>14</id>
</cocktail>
<cocktail name="B-52" id="4">
<name>B-52</name>
<id>4</id>
</cocktail>
</data>
I want to remove a cocktail Element where the id-attribute is 4, which is stored in a variable.
How can I tell my app that it only has to delete the cocktail Element where the id is 4?
XmlNode.RemoveChild will not work since it isn't supported/available in Windows Phone 8.
I wrote the following code but I'm stuck on where to write the id of the element I want to remove.
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Resources;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using XmlLocalDelete1.Resources;
using System.Xml;
using System.Xml.Linq;
using System.Text;
namespace XmlLocalDelete1
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
private string id = "4";
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
try
{
tb1.Text = "";
// copy the xml file to isolated storage
using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!file.FileExists("bar.xml"))
{
StreamResourceInfo sr_en = Application.GetResourceStream(new Uri("Resources\\bar.xml", UriKind.Relative));
using (BinaryReader br_en = new BinaryReader(sr_en.Stream))
{
byte[] data = br_en.ReadBytes((int)sr_en.Stream.Length);
//Write the file.
using (BinaryWriter bw = new BinaryWriter(file.CreateFile("bar.xml")))
{
bw.Write(data);
bw.Close();
}
}
}
// work with file at isolatedstorage
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("bar.xml", FileMode.Open, file))
{
XDocument doc = XDocument.Load(stream, LoadOptions.None);
// delete node
XElement deleteThis = doc.Element("cocktail");
deleteThis.Remove();
}
// Write remaining Xml to textblock
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("bar.xml", FileMode.Open, file))
{
// Load the XML file
XmlReader reader = XmlReader.Create(stream);
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: // Het knooppunt is een element.
//tb1.Text = tb1.Text + "<" + reader.Name;
while (reader.MoveToNextAttribute()) // Attributen lezen.
tb1.Text = tb1.Text + " " + reader.Name + "='" + reader.Value + "'";
//tb1.Text = tb1.Text + ">";
break;
case XmlNodeType.Text: //De tekst in elk element weergeven.
//tb1.Text = tb1.Text + reader.Value + "\r\n";
Console.WriteLine(reader.Value);
break;
case XmlNodeType.EndElement: //Het einde van het element weergeven.
Console.Write("</" + reader.Name);
Console.WriteLine(">");
break;
}
}
reader.Close();
}
}
}
catch (Exception myExc)
{
Console.WriteLine(myExc.Message);
}
}
}
}

I want to remove a cocktail Element where the id-attribute is 4,
var xDoc = XDocument.Load(filename); //or XDocument.Load(stream);
xDoc.Descendants("cocktail").First(c => c.Attribute("id").Value == "4").Remove();
string newXml = xDoc.ToString();
OR using XPATH
xDoc.XPathSelectElement("//cocktail[#id='4']").Remove();

I would recommend you use XDocument instead XmlReader. In that case the task will be much easier.
XDocument xdoc = XDocument.Load(filename);
xdoc.Root.Elements().Where(x => x.Attribute("id").Value == "4").Remove();
xdoc.Save(filename);

I would use the XmlReader only for large xml files.
Searching and manipulation xml files is much easier with XmlDocument or better XDocument.

Related

"The given path's format is not supported" Error when trying to pull XML into Treeview

Currently trying to get a XML file from a location on my machine to display to my Treeview. I pretty much used the code from another stackoverflow question:
Recursion, parsing xml file with attributes into treeview c#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
string Path = Application.StartupPath + #"C:\Users\apearson\Documents\Works.xml";
public Form1()
{
InitializeComponent();
DisplayTreeView(Path);
}
private void DisplayTreeView(string pathName)
{
try
{
XmlDocument dom = new XmlDocument();
dom.Load(pathName);
treeView1.Nodes.Clear();
foreach (XmlNode xNode in dom.ChildNodes)
{
var tNode = treeView1.Nodes[treeView1.Nodes.Add(new TreeNode(xNode.Name))];
AddNode(xNode, tNode);
}
}
catch (XmlException xmlEx)
{
MessageBox.Show(xmlEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
{
if (inXmlNode is XmlElement)
{
foreach (var att in inXmlNode.Attributes.Cast<XmlAttribute>().Where(a => !a.IsDefaultNamespaceDeclaration()))
{
inTreeNode.Text = inTreeNode.Text + " " + att.Name + ": " + att.Value;
}
var nodeList = inXmlNode.ChildNodes;
foreach (XmlNode xNode in inXmlNode.ChildNodes)
{
var tNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(xNode.Name))];
AddNode(xNode, tNode);
}
}
else
{
inTreeNode.Text = (inXmlNode.OuterXml).Trim();
}
treeView1.ExpandAll();
}
}
When debugging, I noticed that it will stop at the dom.Load(pathName) then go straight to the catch. Then throw me the error of "The given path's format is not supported". I've seen some other articles with this issue but nothing with a Treeview so don't know if they would have been much help. Is there something I'm missing?
This part
string Path = Application.StartupPath + #"C:\Users\apearson\Documents\Works.xml
... will concatenate the startup path with the full path you define as literal.
This will lead to something like
C:\blah\blub\C:\Users\apearson\Documents\Works.xml
...which is not a valid path..

Get value from simple XML document

I have the following XML
<User
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/GaryLeaderboardsAPI.Models">
<Game_ID>3</Game_ID>
<UserGUID>e00d3560-4133-4ba6-8bba-e6c8659468b4</UserGUID>
<UserName>tony2</UserName>
<User_ID>16</User_ID>
</User>
Using C# I am loading this into an XMLDocument, How do I retrieve the UserGUID value?
Leveraging System.Xml.Linq you could do
string xml = "..."; // your inline XML
var doc = System.Xml.Linq.XDocument.Parse(xml);
or
string xmlFile = "..."; // your XML filename
var doc = System.Xml.Linq.XDocument.Load(xmlFile);
and then, to get the UserGUID
var userGuid = doc.Descendants().Where(x=>x.Name.LocalName == "UserGUID").First().Value;
This will work for you.
string xml = "<User xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/GaryLeaderboardsAPI.Models\">" +
"<Game_ID>3</Game_ID>" +
"<UserGUID>e00d3560-4133-4ba6-8bba-e6c8659468b4</UserGUID>" +
"<UserName>tony2</UserName>" +
"<User_ID>16</User_ID>" +
"</User>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var id = doc.GetElementsByTagName("UserGUID")[0].InnerText;
You need to use the namespace. I often use mipnw approach (probably stole idea from one of my postings). See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement user = doc.Root;
XNamespace ns = user.GetDefaultNamespace();
string UserGUID = (string)user.Element(ns + "UserGUID");
}
}
}

How to change value in xml string?

I have the following method
string UpdateXmlString(string xmlString) {...}
I would like to find all tags which name contain password and delete a value;
Before:
<job xmlns:i=\"...\" xmlns=\"...">
<password>asdfasdf</password>
<adminPassword>asd</adminPassword>
...</job>
Expected result:
<job xmlns:i=\"..." xmlns=\"...">
<password></password>
<adminPassword></adminPassword>
...</job>
How to implement this method?
You should simply be using XmlDocument or XDocument to parse this. I wouldn't manipulate XML strings manually.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace XDocumentTest
{
class Program
{
static void Main(string[] args)
{
try
{
String xml = "<?xml version=\"1.0\"?><rootElement>";
xml += "<user id=\"1\"><password>temp</password></user>";
xml += "<user id=\"2\"><adminPassword>foobar</adminPassword></user>";
xml += "<user id=\"3\"><somePassWORDelement>foobarXYZ</somePassWORDelement></user>";
xml += "</rootElement>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants().Where(
e => e.Name.ToString().ToLower().Contains("password")))
{
Console.WriteLine(element);
// Delete your value here. Either changing the text node
// or by removing the password node. E.g.
element.Value = string.Empty;
}
Console.WriteLine(doc.ToString());
}
catch (Exception e)
{
Console.WriteLine(e);
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
}
}
}
}
You should use XPathNavigator.
In MSDN are some examples that will help you:
https://msdn.microsoft.com/en-us/library/zx28tfx1(v=vs.110).aspx
var doc = XDocument.Load(path);
var element = doc.Descendants("YOUR_Descendants")
.Where(arg => arg.Attribute("ExampleID").Value == "3" )//
.Single();
element.Element("UpdateElements")
.Element("UpdateElements_fc").Value = "222";// update
doc.Save(path); //save

Writing online XML file to textbox

I am using a PHP script to generate xml files. I want to write the data in the XML file to a Textblock in my Windows Phone 8 App.
When I debug, I get an error which is not caught my the catch. A print screen of the error: http://i811.photobucket.com/albums/zz38/JelleK1996/errorxml1_zps20df0a45.png
What is wrong?
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Xml;
using System.IO;
using System.Xml.Linq;
using System.Diagnostics;
namespace xml1
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
try
{
HttpWebRequest request = WebRequest.Create("http://cocktailpws.net23.net/requests/get_cocktail.php?id=10") as HttpWebRequest;
request.BeginGetResponse(r =>
{
var reponse = request.EndGetResponse(r);
//XDocument xmlDoc = XDocument.Load(reponse.GetResponseStream());
XmlReader reader = XmlReader.Create(reponse.GetResponseStream());
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: // Het knooppunt is een element.
Console.Write("<" + reader.Name);
Console.WriteLine(">");
break;
case XmlNodeType.Text: //De tekst in elk element weergeven.
tb1.Text = tb1.Text + reader.Value + "\r\n";
Console.WriteLine(reader.Value);
break;
case XmlNodeType.EndElement: //Het einde van het element weergeven.
Console.Write("</" + reader.Name);
Console.WriteLine(">");
break;
}
}
}, null);
}
catch (Exception myExc)
{
Console.WriteLine(myExc.Message);
}
}
}
}
Firstly you have not got try catch block inside lambda function. Thats why you cant handle error
Secondly how to solve:
change :
tb1.Text = tb1.Text + reader.Value + "\r\n";
to
Dispatcher.BeginInvoke(() => {
tb1.Text = tb1.Text + reader.Value + "\r\n";
});
Thirdly,
I Believe you need rewrite your xml loop, Cause your code is inefficent way. It will call text change if there are many texts.So build string then after loop call text change
StringBuilder res = new StringBuilder();
...
//inside xml loop:
res.AppendLine(reader.Value);
...
//after loop:
Dispatcher.BeginInvoke(() => {
tb1.Text = res.ToString();
});
And check this to see what was error. or search invalid thread call
invalid thread call
I believe you must access tb1 from the UI thread, so I would suggest trying to use a statement similar to this:
case XmlNodeType.Text: //De tekst in elk element weergeven.
{
tb1.Dispatcher.BeginInvoke(() =>
{
tb1.Text = tb1.Text + reader.Value + "\r\n";
});
Console.WriteLine(reader.Value);
}
break;

Use XMLReader to access child nodes with duplicate names

Using the XML fragment below, how would I access the child node <amt> of the node <salesTaxAmt> using XMLReader? If I iterate over the nodes looking for a node name of "amt" I return the last node amount of <sourceCurrAmt> which is 0.00.
<transactionUnit>
<transactionDetails>
<transactionId>11883382</transactionId>
<currencyAmount>
<amt>30.00</amt>
<currCode>USD</currCode>
</currencyAmount>
<gstAmount>
<amt>60.00</amt>
<currCode>USD</currCode>
</gstAmount>
<pstNqstAmt>
<amt>0.00</amt>
<currCode>USD</currCode>
</pstNqstAmt>
<salesTaxAmt>
<amt>1.00</amt>
<currCode>USD</currCode>
</salesTaxAmt>
<sourceCurrAmt>
<amt>0.00</amt>
</sourceCurrAmt>
</transactionDetails>
</transactionUnit>
Is the code below even the best way to do this?
Test Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
namespace TestConsole
{
public class ParseXML
{
static void Main(string[] args)
{
try
{
FileStream file;
XmlReader baseReader;
XmlTextReader reader;
XmlReaderSettings readerSettings;
file = new FileStream(#"C:\Data.xml", FileMode.Open, FileAccess.Read);
file.Seek(0, SeekOrigin.Begin);
reader = new XmlTextReader(file, XmlNodeType.Element, null);
reader.Normalization = false;
readerSettings = new XmlReaderSettings();
readerSettings.ConformanceLevel = ConformanceLevel.Fragment;
readerSettings.IgnoreWhitespace = false;
readerSettings.IgnoreComments = true;
readerSettings.CheckCharacters = false;
baseReader = XmlReader.Create(reader, readerSettings);
int x = 0;
while (baseReader.Read())
{
if (baseReader.Name.Equals("transactionUnit") && (baseReader.NodeType == XmlNodeType.Element))
{
string amt = null;
XmlReader inner = reader.ReadSubtree();
while (inner.Read())
{
if (inner.Name.Equals("ns:amt"))
{
amt = inner.ReadElementString();
}
}
Console.WriteLine("amt: {0}", amt);
inner.Close();
x = x + 1;
}
}
Console.WriteLine("{0} transactions found", x.ToString());
baseReader.Close();
file.Close();
}
catch (XmlException xe)
{
Console.WriteLine("XML Parsing Error: " + xe);
}
catch (IOException ioe)
{
Console.WriteLine("File I/O Error: " + ioe);
}
}
}
}
From your example code it looks like you can use LINQ to XML, this would be a more succinct solution for your problem:
XDocument xDoc = XDocument.Load(#"C:\Data.xml");
string amt = xDoc.Descendants("salesTaxAmt")
.Elements("amt")
.Single().Value;
If you can use it LINQ to XML should definitely be your choice, it provides an easy and powerful syntax for retrieving data from XML and plays nicely with LINQ to objects.
A simpler way to get the node value is to use XPath.
Note that this is possible only if you node the path to the node whose value is to be retreived.
private string GetNodeValue(string xmlFilePath, string xpath)
{
string nodeValue = string.Empty;
using (StreamReader reader = new StreamReader(xmlFilePath))
{
XPathDocument xPathDocument = new XPathDocument(reader);
XPathNavigator navigator = xPathDocument.CreateNavigator();
XPathNodeIterator iter = navigator.Select(xpath);
iter.MoveNext();
nodeValue = iter.Current.Value;
//iter.Current.ValueAsDouble;
}
return nodeValue;
}
The usage for your sample xml should be:
string nodeValue = GetNodeValue(#"C:\Data.xml", "//transactionUnit/transactionDetails/salesTaxAmt/amt");
In addition, you could rename the method as 'GetNodeValueAsDouble' and use iter.Current.ValueAsDouble instead of iter.Current.Value to get the double value.
More Information
XPathNavigator class
XPathDocument class

Categories