Deleting whole sections of my XML file - c#

I have a userlist for a program I'm designing, and all the users are stored to an XML file, like so:
<?xml version="1.0"?>
<Users>
<User ID="1">
<nickname>Tom</nickname>
<password>Sams</password>
<host>ahost#asd.com</host>
<email>badrandom#as.com</email>
<isloggedin>true</isloggedin>
<permission>10</permission>
</User>
<User ID="2">
<nickname>ohai</nickname>
<password>asdalkdj9u</password>
<host>meh#meh.com</host>
<email>my#email</email>
<isloggedin>false</isloggedin>
<permission>1</permission>
</User>
<User ID="3">
<nickname>ohai</nickname>
<password>sercret</password>
<host>my#host</host>
<email>my#email</email>
<isloggedin>false</isloggedin>
<permission>1</permission>
</User>
<User ID="4">
<nickname>mib_hr6qhr</nickname>
<password>YXNsa2RhZGxrYXNk</password>
<host>adb7e51b#webchat.mibbit.com</host>
<email>alskd#alskd.com</email>
<isloggedin>true</isloggedin>
<permission>1</permission>
</User>
</Users>
Now, based on the users ID number, I need to be able to delete all reference to that user.
So say, I have ID number 3, how can I completely delete userid number 3's existence from the xml file?
I'm looking for code examples, but any help would be greatly appreciated!

One approach using the XML DOM (.NET 1.x and up) would be to just load the file, find user no. 3, and remove that node, and save the file back:
XmlDocument doc = new XmlDocument();
doc.Load("yourXmlFile.xml");
XmlNode userNo3 = doc.SelectSingleNode("//Users/User[#ID='3']");
if(userNo3 != null)
{
userNo3.ParentNode.RemoveChild(userNo3);
}
doc.Save("YourNewXmlFile.xml");
Marc

Assuming you have the XML loaded in an XDocument:
using System.Linq;
using System.Xml.Linq;
void Delete()
{
XDocument document = LoadXML();
document.Elements("Users")
.Single(e => e.Attribute("ID").Value == "3")
.Remove();
}
Of course, this assumes that the user you request will always be present in the XML. To be safe, you should use SingelOrDefault() and check for a null value before deleting.

The problem with linq and DOM approach is that there is a round-trip (parsing/serialization) which can be efficient, vtd-xml has something called incremental update, here is an article that explains it... http://www.codeproject.com/KB/XML/xml_processing_future.aspx
VTDGen vg = new VTDGen();
if (vg.parseFile("your.xml", false)){
VTDNav vn = vg.getNav();
AutoPilot ap = new AutoPilot(vn);
ap.selectXPath("/users/user[#ID='3']");
XMLModifier xm = new XMLModifier(vn);
if (ap.evalXPath()!=-1){
xm.remove();
xm.output("new.xml");
}
}

Related

Count XML Nodes Script Task SSIS

I have the below xml:
<Users>
<User ID="User1"
<Element1>Name1<Element1>
</User>
<User ID="User2"
<Element1>Name2<Element1>
</User>
<User ID="User3"
<Element1>Name3<Element1>
</User>
</Users>
This XML is created and stored in a string variable in SSIS.
I need to count the User nodes before doing the next step and I would like to do it using a script task. This what I think I need to do
// assign string to a variable
String XMLString = Dts.Variables["User::XMLString"].Value
// Convert variable to xml data type:
XmlSerializer serializer = new XmlSerializer(XMLString );
//Count the user nodes and assign integer to a variable.
Dts.Variables["User::UserCount"].Value = serializer.SelectNodes("Users/User").Count;
I’ve been trying to figure out how to do it but I’m quite new on C# and I would really need an example about this please.
Thanks!
You can do this:
string XMLString = Dts.Variables["User::XMLString"].Value;
Dts.Variables["User::UserCount"].Value = (Regex.Matches(XMLString, #"</User>")).Count;
See the code here in this link.
Try this:
XmlDocument doc = new XmlDocument();
doc.Load(XMLString);
Dts.Variables["User::UserCount"].Value = doc.SelectNodes("User").Count;

Loading Descendants of XML using XDocument.Load

I am attempting to use XDocument.Load to access some latitude and longitude figures. Here is the example XML document;
<?xml version="1.0" encoding="utf-8"?>
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/search/local/ws/rest/v1">
<Copyright>Copyright © 2016 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.</Copyright>
<BrandLogoUri>http://dev.virtualearth.net/Branding/logo_powered_by.png</BrandLogoUri>
<StatusCode>200</StatusCode>
<StatusDescription>OK</StatusDescription>
<AuthenticationResultCode>ValidCredentials</AuthenticationResultCode>
<TraceId>31a206847f9341d28689e0e7185e163d|DB40051719|7.7.0.0|DB4SCH010061262</TraceId>
<ResourceSets>
<ResourceSet>
<EstimatedTotal>1</EstimatedTotal>
<Resources>
<Location>
<Name>SW1A 1AA, London, London, United Kingdom</Name>
<Point>
<Latitude>51.501018524169922</Latitude>
<Longitude>-0.14159967005252838</Longitude>
</Point>
<BoundingBox>
<SouthLatitude>51.497155806599245</SouthLatitude>
<WestLongitude>-0.14987251765942367</WestLongitude>
<NorthLatitude>51.5048812417406</NorthLatitude>
<EastLongitude>-0.1333268224456331</EastLongitude>
</BoundingBox>
<EntityType>Postcode1</EntityType>
<Address>
<AdminDistrict>England</AdminDistrict>
<AdminDistrict2>London</AdminDistrict2>
<CountryRegion>United Kingdom</CountryRegion>
<FormattedAddress>SW1A 1AA, London, London, United Kingdom</FormattedAddress>
<Locality>London</Locality>
<PostalCode>SW1A 1AA</PostalCode>
</Address>
<Confidence>High</Confidence>
<MatchCode>Good</MatchCode>
<GeocodePoint>
<Latitude>51.501018524169922</Latitude>
<Longitude>-0.14159967005252838</Longitude>
<CalculationMethod>Rooftop</CalculationMethod>
<UsageType>Display</UsageType>
</GeocodePoint>
</Location>
</Resources>
</ResourceSet>
</ResourceSets>
</Response>
And here is the code I am using attempting to access latitude and longitude;
string latitude = XDocument.Load(#"test.xml").Root
.Descendants("ResourceSets")
.Descendants("ResourceSet")
.Descendants("Resources")
.Descendants("Location")
.Descendants("GeocodePoint")
.Select(element => element.Attribute("Latitude").Value).FirstOrDefault();
But this returns an empty string. How can I navigate the document correctly?
First thing you don't need to call in multiples level Descendants method if you want to get all GeocodePoint nodes. You can only do this:
XNamespace ns = "http://schemas.microsoft.com/search/local/ws/rest/v1";
string latitude = XDocument.Load(#"test.xml")
.Descendants(ns+"GeocodePoint")
.Select(e=> (string)e.Element(ns+"Latitude"))
.FirstOrDefault();
With that call Linq to XML will retrieve all the GeocodePoints in your xml
If you want to get lat and long values, then you can project either to an anonymous type or a custom class (DTO) like this:
XNamespace ns = "http://schemas.microsoft.com/search/local/ws/rest/v1";
var coord= XDocument.Load(#"xml.xml")
.Descendants(ns+"GeocodePoint").Select(e => new { Lat = (string)e.Element(ns+"Latitude"), Lng = (string)e.Element(ns+"Longitude") })
.FirstOrDefault();
About your issue
Your problem was you were calling Attribute method to get the Latitude value, but as you can see in your xml structure GeocodePoint node doesn't have that as an attribute, it is a nested element. That's way you need to use Element method instead. The second issue was you need to take the namespace into account as I show above.
You are not using namespace. Your Xml provided with namespace
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.microsoft.com/search/local/ws/rest/v1">
So you need to use it when searching for elements.
XDocument doc = XDocument.Load(#"C:\tmp\data.xml");
XNamespace ns = doc.Root.Name.Namespace;
string value = doc.Root.Descendants(ns + "Latitude").FirstOrDefault().Value;
Or search without namespace, by LocalName of element
string value = doc.Root
.Descendants
.Where(element => element.Name.LocalName.Equals("Latitude"))
.FirstOrDefault()
.Value;
If you using Descendats method, then you can search straight for element you need.

Delete XML node that contains a certain value

Here is the xml structure.
I am trying to delete each Status node where State contains the word failed.
What is the best way to remove these?
<Stats>
<Status>
<Desc>something here</Desc>
<State>pending - ok</State>
</Status>
<Status>
<Desc>something here</Desc>
<State>failed</State>
</Status>
</Stats>
void Main()
{
XDocument xml = XDocument.Parse(#"<Stats>
<Status>
<Desc>something here</Desc>
<State>pending - ok</State>
</Status>
<Status>
<Desc>something here</Desc>
<State>failed</State>
</Status>
</Stats>");
xml.Descendants("State").Where (x => x.Value.Contains("fail")).Ancestors("Status").Remove();
Console.WriteLine(xml.ToString());
}
Parse will load the xml in-memory, Load is used for loading it from a stream or via I/O means.
#Gregory Pilar's answer heavily influenced this answer; I believe he wrote that from memory, the snippet I provided was testing via LinqPad and returns expected results.
You can use Linq to XMl, to do the job
var xdoc = XDocument.Load(path_to_xml);
xdoc.Descendants("Status")
.Where(os => (int)os.Attribute("State") == "failed")
.Remove();
xdoc.Save(path_to_xml);
xDoc.Descendants("Status").Where(status => status.Element("State").Value.ToLower().Contains("fail")).Remove();

C# Parsing specific xml

I wonder how do I parse a specific person by the id in the xml below?
Also lets say I wanna loop through them all and add them to a listview, How do I do that with XmlDocument?
<users>
<user id="Marcus">
<website>www.google.com</website>
<type>1</type>
</user>
<user id="John">
<website>www.youtube.com</website>
<type>1</type>
</user>
<user id="Josh">
<website>www.google.com</website>
<type>2</type>
</user>
</users>
Here's a linq to xml example -
using System.Xml.Linq;
var doc = XDocument.Parse(#"...");
var element = doc.XPathSelectElement("/users/user[#id='John']");
var website = element.XPathSelectElement("website").Value;
var type = int.Parse(element.XPathSelectElement("type").Value);

XML comparison for checking whether same or different window applications

I have two XML file sitemap.xml and mouse.xml which look like below.Here the thing is that
i need to compare sitemap.xml with mouse.xml in such a way that the tag
<Name></Name>.I need to compare both xml file whether the content
coming inside <Name></Name> tag is same or not in c# code
Here the <Name></Name> tag are different means sitemap.xml contain "test
" and mouse.xml contain "exam".
<?xml version="1.0" standalone="yes"?>
<ObjectClass>
<Image>00000000-0000-0000-0000-000000000000</Image>
<Description />
<Name>test</Name>
<DefaultApp>00000000-0000-0000-0000-000000000000</DefaultApp>
<ID>464930eb-e518-4d0c-b80b-184c97c7dd27</ID>
<ParentClassID>00000000-0000-0000-0000-000000000002</ParentClassID>
<DynamicPopulation>false</DynamicPopulation>
<TimeoutPeriod>0</TimeoutPeriod>
<Persist>false</Persist>
<ClassVersion>1</ClassVersion>
<Reinitialize>false</Reinitialize>
</ObjectClass>
this is mouse.xml
<?xml version="1.0" standalone="yes"?>
<ObjectClass>
<Image>00000000-0000-0000-0000-000000000000</Image>
<Description />
<Name>exam</Name>
<DefaultApp>00000000-0000-0000-0000-000000000000</DefaultApp>
<ID>464930eb-e518-4d0c-b80b-184c97c7dd27</ID>
<ParentClassID>00000000-0000-0000-0000-000000000002</ParentClassID>
<DynamicPopulation>false</DynamicPopulation>
<TimeoutPeriod>0</TimeoutPeriod>
<Persist>false</Persist>
<ClassVersion>1</ClassVersion>
<Reinitialize>false</Reinitialize>
</ObjectClass>
Try the Microsoft XML diff API.
Try,
XmlDocument doc1 = new XmlDocument();
XmlDocument doc2 = new XmlDocument();
doc1.Load(#"c:\myproject\WindowsApplication1\sitemap.xml");
doc2.Load(#"c:\myproject\WindowsApplication1\mouse.xml");
XmlNodeList a = doc1.GetElementsByTagName("Name");
XmlNodeList b = doc2.GetElementsByTagName("Name");
if (a.Count == 1 && b.Count == 1)
{
if (a[0].InnerText == b[0].InnerText)
Console.WriteLine("Equal");
else
Console.WriteLine("Not Equal");
}
XMLUnit is awesome for xml comparison. Primarily Java based but there is a .Net port there too (I've only used the Java one): http://xmlunit.sourceforge.net/

Categories