Remove all nodes in a specified namespace from XML - c#

I have an XML document that contains some content in a namespace. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<test:AlternativeName>Another name</test:AlternativeName>
<Price test:Currency="GBP">124.00</Price>
</Item>
</root>
I want to remove all of the content that is within the test namespace - not just remove the namespace prefix from the tags, but actually remove all nodes (elements and attributes) from the document that (in this example) are in the test namespace. My required output is:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>
I'm currently not overly concerned if the namespace declaration is still present, for now I'd be happy with removing just the content within the specified namespace. Note that there may be multiple namespaces in the document to be modified, so I'd like to be able to specify which one I want to have the content removed.
I've tried doing it using .Descendants().Where(e => e.Name.Namespace == "test") but that is only for returning an IEnumerable<XElement> so it doesn't help me with finding the attributes, and if I use .DescendantNodes() I can't see a way of querying the namespace prefix as that doesn't seem to be a property on XNode.
I can iterate through each element and then through each attribute on the element checking each one's Name.Namespace but that seems inelegant and hard to read.
Is there a way of achieving this using LINQ to Xml?

Iterating through elements then through attributes seems not too hard to read :
var xml = #"<?xml version='1.0' encoding='UTF-8'?>
<root xmlns:test='urn:my-test-urn'>
<Item name='Item one'>
<test:AlternativeName>Another name</test:AlternativeName>
<Price test:Currency='GBP'>124.00</Price>
</Item>
</root>";
var doc = XDocument.Parse(xml);
XNamespace test = "urn:my-test-urn";
//get all elements in specific namespace and remove
doc.Descendants()
.Where(o => o.Name.Namespace == test)
.Remove();
//get all attributes in specific namespace and remove
doc.Descendants()
.Attributes()
.Where(o => o.Name.Namespace == test)
.Remove();
//print result
Console.WriteLine(doc.ToString());
output :
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>

Give this a try. I had to pull the namespace from the root element then run two separate Linqs:
Removes elements with the namespace
Removes attributes with the namespace
Code:
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<root xmlns:test=\"urn:my-test-urn\">" +
"<Item name=\"Item one\">" +
"<test:AlternativeName>Another name</test:AlternativeName>" +
"<Price test:Currency=\"GBP\">124.00</Price>" +
"</Item>" +
"</root>";
XDocument xDocument = XDocument.Parse(xml);
if (xDocument.Root != null)
{
string namespaceValue = xDocument.Root.Attributes().Where(a => a.IsNamespaceDeclaration).FirstOrDefault().Value;
// Removes elements with the namespace
xDocument.Root.Descendants().Where(d => d.Name.Namespace == namespaceValue).Remove();
// Removes attributes with the namespace
xDocument.Root.Descendants().ToList().ForEach(d => d.Attributes().Where(a => a.Name.Namespace == namespaceValue).Remove());
Console.WriteLine(xDocument.ToString());
}
Results:
<root xmlns:test="urn:my-test-urn">
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>
If you want to remove the namespace from the root element add the this line in the if statement after you get the namespaceValue
xDocument.Root.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
Results:
<root>
<Item name="Item one">
<Price>124.00</Price>
</Item>
</root>

Related

Change XML element value in c#

My C# code:
XDocument doc = XDocument.Load(filename);
IEnumerable<XElement> collection =
doc.Elements("BCIRequest").Elements("Card").Elements("SelectedPIN");
My XML document:
<?xml version="1.0" encoding="utf-8"?>
<BCIRequest Version="2.0"
xmlns="urn:xxxxxx:bci:request">
<Header>
<SenderCode>XX99</SenderCode>
<SenderID>9999</SenderID>
<SequenceNumber>123</SequenceNumber>
<CardGroupCount>2</CardGroupCount>
<CardCount>4</CardCount>
<BlockCount>2</BlockCount>
</Header>
<!--card groups (must precede cards and blocks)-->
<CardGroup RequestID="1">
<CustomerNumber>XX01234567</CustomerNumber>
<CardGroupName Emboss="true">GROUP ONE</CardGroupName>
</CardGroup>
<CardGroup RequestID="2"
RequestRef="87416CB7-DAEF-483A-BD08-1A885531D958">
<CustomerNumber>XX12345678</CustomerNumber>
<CardGroupName Emboss="false">GROUP TWO</CardGroupName>
</CardGroup>
<Card RequestID="3">
<CustomerNumber>XX01234567</CustomerNumber>
<DriverCard>
<Driver Emboss="true">MARGE SIMPSON</Driver>
</DriverCard>
<CardTypeID>10</CardTypeID>
<PurchaseCategoryID>11</PurchaseCategoryID>
<Reissue>false</Reissue>
<GeneratedPIN/>
<OdoPrompt>false</OdoPrompt>
<CRNPrompt>false</CRNPrompt>
</Card>
<Card RequestID="4">
<CustomerNumber>XX12345678</CustomerNumber>
<VehicleCard>
<VRN Emboss="true">KYI 830</VRN>
</VehicleCard>
<CardTypeID>10</CardTypeID>
<PurchaseCategoryID>11</PurchaseCategoryID>
<Reissue>false</Reissue>
<SelectedPIN>0123</SelectedPIN>
<OdoPrompt>false</OdoPrompt>
<CRNPrompt>false</CRNPrompt>
</Card>
<Card RequestID="5">
<CustomerNumber>XX01234567</CustomerNumber>
<BearerCard>
<Bearer Emboss="true">OPEN XXXXXX</Bearer>
</BearerCard>
<CardTypeID>10</CardTypeID>
<PurchaseCategoryID>11</PurchaseCategoryID>
<Reissue>false</Reissue>
<FleetPIN/>
<OdoPrompt>false</OdoPrompt>
<CRNPrompt>false</CRNPrompt>
</Card>
<Block RequestID="6">
<CustomerNumber>XX01234567</CustomerNumber>
<PAN>7002999999999999991</PAN>
</Block>
<Card RequestID="7"
RequestRef="956EA6C5-7D7E-4622-94D0-38CAD9FCC8DF">
<CustomerNumber>XX01234567</CustomerNumber>
<DriverCard>
<Driver Emboss="true">HOMER SIMPSON</Driver>
<VRN Emboss="true">795 DVI</VRN>
</DriverCard>
<EmbossText>SPRINGFIELD POWER</EmbossText>
<CardTypeID>10</CardTypeID>
<TokenTypeID>20</TokenTypeID>
<PurchaseCategoryID>30</PurchaseCategoryID>
<ExpiryDate>2018-12</ExpiryDate>
<Reissue>true</Reissue>
<SelectedPIN>0123</SelectedPIN>
<OdoPrompt>true</OdoPrompt>
<CRNPrompt>true</CRNPrompt>
<!--address with optional fields specified-->
<CardDeliveryAddress OneTimeUse="false">
<ContactName>M xxxx</ContactName>
<ContactTitle>Mr</ContactTitle>
<CompanyName>Sxxxx</CompanyName>
<Line1>Sector 22-F</Line1>
<Line2>Springfield Power Plant</Line2>
<Line3>xxx Road</Line3>
<City>xxxx</City>
<Zipcode>xxxx</Zipcode>
<CountryCode>xxx</CountryCode>
</CardDeliveryAddress>
<!--address with only required fields-->
<PINDeliveryAddress OneTimeUse="true">
<Line1>xxxx</Line1>
<City>xxx</City>
<Zipcode>xxxx</Zipcode>
<CountryCode>xxxx</CountryCode>
</PINDeliveryAddress>
<Limits>
<Value Transaction="unlimited" Daily="200" Weekly="unlimited" Monthly="400"/>
<Volume Transaction="100" Daily="unlimited" Weekly="unlimited" Monthly="unlimited"/>
<Transactions Daily="unlimited" Weekly="unlimited" Monthly="unlimited"/>
<Day Monday="true" Tuesday="true" Wednesday="true" Thursday="true" Friday="true" Saturday="false" Sunday="false"/>
<Time Start="unlimited" End="17:00:00"/>
</Limits>
<Products>
<FuelProductRestrictionID>40</FuelProductRestrictionID>
<NonFuelProductRestrictionID>51</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>52</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>53</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>54</NonFuelProductRestrictionID>
<NonFuelProductRestrictionID>55</NonFuelProductRestrictionID>
</Products>
</Card>
<Block RequestID="8"
RequestRef="69A3E44D-DC10-4BEE-9249-1FC3C651BA0E">
<CustomerNumber>xxxxx</CustomerNumber>
<PAN>xxxxxx</PAN>
</Block>
</BCIRequest>
I need to update the element value in the above values. The old value is:
<SelectedPIN>0123</SelectedPIN>
And the new value should be:
<SelectedPIN EncryptedPIN="TKDS" FormNumber="000793906306">****</SelectedPIN>
Can anyone can help me on this?
If I selected the BCIRequest element, it's returning a null value. I've tried many solutions but unable to get one working on this XML file.
There many ways an Xml can be be modified, I prefer XDocument
XDocument doc = XDocument.Parse(input);
foreach (var element in doc.Descendants("SelectedPIN")) // filter if you want single element, in example I modifed for all elements.
{
element.Add(new XAttribute("EncryptedPIN", "TKDS"));
element.Add(new XAttribute("FormNumber", "000793906306"));
element.Value = "xxxxx"; //new value
}
and finally you can save the document using
doc.Save();
Take a look at this Demo
The root node (BCIRequest) contains a namespace so you need to include that into your query. Something like this should work:
XNamespace ns = "urn:xxxxxx:bci:request";
IEnumerable<XElement> collection = doc.Elements(ns + "BCIRequest").Elements(ns + "Card").Elements(ns + "SelectedPIN");

Get entire node XML rather than InnerXml

Given the following XML:
<Root>
<Item id="1">
<name>Foo</name>
<status>Active</status>
</Item>
<Item id="2">
<name>Bar</name>
<status>Inactive</status>
</Item>
</Root>
Let's say I have this XML in an XmlDocument object and then have the following code:
var nodes = xmlDocumentObject.GetElementsByTagName("Item");
foreach (var node in nodes)
{
var nodeXml = ??
}
I can easily get the InnerXml of each node, which for the first node would be:
<name>Foo</name>
<status>Active</status>
But how can I get the XML for the node including the containing tag and its attributes, such as this:
<Item id="1">
<name>Foo</name>
<status>Active</status>
</Item>
Try using XmlNode.OuterXml instead of InnerXml :
foreach (XmlNode node in nodes)
{
var nodeXml = node.OuterXml;
}

Getting null descendants using linq to xml

I'm calling a web service and using Webresponse.GetResponseStream to get the XML below.
I'm having no luck using Linq-to-XML to get the <Item> and <Title> elements. I'm getting null back when using Descendants.
Can someone suggest what I can try to get these elements?
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<Items>
<Request>
<IsValid>True</IsValid>
<ItemSearchRequest>
<ResponseGroup>Small</ResponseGroup>
<SearchIndex>DVD</SearchIndex>
<Title>Jaws</Title>
</ItemSearchRequest>
</Request>
<Item>
<ASIN>B007STBUIW</ASIN>
<DetailPageURL>http://www.amazon.com/Jaws-Blu-ray-Digital-Copy-UltraViolet/dp/B007STBUIW%3FSubscriptionId%3DAKIAIR3KWWR37IS5MTBA%26tag%3Dfli048-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB007STBUIW</DetailPageURL>
<ItemLinks>
<ItemLink>
<Description>Technical Details</Description>
<URL>http://www.amazon.com/Jaws-Blu-ray-Digital-Copy-UltraViolet/dp/tech-data/B007STBUIW%3FSubscriptionId%3DAKIAIR3KWWR37IS5MTBA%26tag%3Dfli048-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB007STBUIW</URL>
</ItemLink>
<ItemLink>
<Description>Add To Baby Registry</Description>
<URL>http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3DB007STBUIW%26SubscriptionId%3DAKIAIR3KWWR37IS5MTBA%26tag%3Dfli048-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB007STBUIW</URL>
</ItemLink>
</ItemLinks>
<ItemAttributes>
<Actor>Roy Scheider</Actor>
<Creator Role="Writer">Carl Gottlieb</Creator>
<Director>Steven Spielberg</Director>
<Title>Jaws (Blu-ray + DVD + Digital Copy + UltraViolet)</Title>
</ItemAttributes>
</Item>
<Item>
<ASIN>B008YKB3X6</ASIN>
<DetailPageURL>http://www.amazon.com/Jaws-Comes-Home-HD/dp/B008YKB3X6%3FSubscriptionId%3DAKIAIR3KWWR37IS5MTBA%26tag%3Dfli048-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB008YKB3X6</DetailPageURL>
<ItemLinks>
<ItemLink>
<Description>Technical Details</Description>
<URL>http://www.amazon.com/Jaws-Comes-Home-HD/dp/tech-data/B008YKB3X6%3FSubscriptionId%3DAKIAIR3KWWR37IS5MTBA%26tag%3Dfli048-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008YKB3X6</URL>
</ItemLink>
</ItemLinks>
<ItemAttributes>
<Creator Role="Producer">Discovery</Creator>
<ProductGroup>TV Series Episode Video on Demand</ProductGroup>
<Title>Jaws Comes Home [HD]</Title>
</ItemAttributes>
</Item>
</Items>
</ItemSearchResponse>
Update:
Thanks Jim and Kelly that was the issue. I am now able to get the value using this code :
var itemtitle = doc2.Root.Element(ns + "Items").Elements(ns + "Item").Elements(ns + "ItemAttributes").Elements(ns + "Title").First();
As Jim said, adding the namespace should work. Here is an example to pull the items.
var xml = XDocument.Parse(xmlstr);
XNamespace ns = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
var items = xml.Root.Element(ns+"Items").Elements(ns+"Item");

Parse XML into String type array in Windows Phone 8 App using C#

I have an XML file, which I like to parse and get the value in a string type array. I know there are XMLSerialization namespace and other things. But what I am trying to achieve is getting the value in a string array. It may be obtained using Foreach loop or for loop.
For example, here is my XML file:
<?xml version="1.0" encoding="utf-8" ?>
<channel>
<title>Social Media</title>
<item>
<title>Facebook</title>
<link>http://www.facebook.com/</link>
</item>
<item>
<title>Twitter</title>
<link>http://www.twitter.com/</link>
</item>
<item>
<title>Google+</title>
<link>http://plus.google.com/</link>
</item>
</channel>
Now, I have two string type array as variable into a C# file.
For example:
public string[] WebsiteName;
public string[] Urls;
Now, I want to get all the values of WebsiteName into the WebsiteName array and website links into the Urls array.
Is there any way to do it? If yes, please show it to me. It will be very helpful.
Here is an example to get website names and links using LINQ:
var xml = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<channel>
<title>Social Media</title>
<item>
<title>Facebook</title>
<link>http://www.facebook.com/</link>
</item>
<item>
<title>Twitter</title>
<link>http://www.twitter.com/</link>
</item>
<item>
<title>Google+</title>
<link>http://plus.google.com/</link>
</item>
</channel>";
var doc = XDocument.Parse(xml);
WebsiteName = doc.Descendants("title").Select(o => o.Value).ToArray();
Urls = doc.Descendants("link").Select(o => o.Value).ToArray();
XDocument.Parse(xml): create XDocument from string. If you want the source is file instead of string then you can use XDocument.Load("path_to_the_xml_file").
doc.Descendants("title"): will get all tags named "title", then
.Select(o => o.Value): will get the string between the opening and closing tag, aka the value
var doc = XDocument.Parse(xml);
var WebsiteName = doc.Descendants("title").Select(o => o.Value).ToArray();
ListBox.ItemsSource = WebsiteName;
And you get content written in listbox.

Xpath path not working

I'm trying to get an element and its siblings via XpathNavigator.
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE document SYSTEM 'xmlschemas/domino_6_5_5.dtd'>
<document xmlns='http://www.lotus.com/dxl' version='6.5' maintenanceversion='5.0'>
<item name='Keywords'><text/></item>
<item name='Version'><number>1</number></item>
<item name='UPDATEDISTRIBUTION'><text>1</text></item>
<item name='$FILE' summary='true' sign='true' seal='true'>
<object>
<file hosttype='cdstorage' compression='none' flags='storedindoc' name='STG08828'>
<created><datetime>20110119T230442,22+01</datetime></created>
</file>
</object>
</item>
</document>
I want to navigate to the file-element, with the following XPath:
//item/name/object/file[#name='STG08828']
Why this path is wrong?
EDIT: Thanks for the hint with my "name"-mistake.
When I try to run it, I get nothing.
XmlElement rootNode = xmlDoc.DocumentElement;
// select the file Element
String query = "//file[#name='" + name + "']";
XmlNodeList fileElement = rootNode.SelectNodes(query);
I think you want:
//item/object/file[#name='STG08828']
Or maybe just:
//file[#name='STG08828']
I think you are missing your names space for xmlns='http://www.lotus.com/dxl'
Example:
XNamespace myMs = "http://www.lotus.com/dxl";

Categories