Parse XML skipping attributes if it does not start with * in c# - c#

I have written something small to output an xml file from and xml file, the output will only contain nodes that are required using xpath, I'm happy with this.
As an example below;
I would like to expand this further and specifically output ALL of the input XML but in node QWE #ID skip any attribute that does not start with AA.
I have tried this with xpath with no luck. I believe I will need to do this in a loop.
Gentle please, we all start out somewhere.
Thanks.
<MMM>
<PPP>
<AAA>
<QWE ID="AABBCC">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
<NAME3>CC</NAME3>
</QWE>
<QWE ID="AACCBB">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
<NAME3>CC</NAME3>
</QWE>
<QWE ID="CCDDKK">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
<NAME3>CC</NAME3>
</QWE>
<QWE ID="CCKKDD">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
<NAME3>CC</NAME3>
</QWE>
</AAA>
<BBB>
<RTY ID="ASDF">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
</RTY>
<RTY ID="ASDG">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
</RTY>
<RTY ID="ASDH">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
</RTY>
<RTY ID="ASDJ">
<NAME1>AA</NAME1>
<NAME2>BB</NAME2>
</RTY>
</BBB>
</PPP>
</MMM>
XmlNodeList nodes = doc.SelectNodes("/*");
XmlElement root = newDoc.CreateElement("MMM");
newDoc.AppendChild(root);
foreach (XmlNode node in nodes)
{
if (node.Name == "QWE")
{
XmlElement newElement = newDoc.CreateElement(node.Name);
root.AppendChild(newElement);
foreach (XmlAttribute attribute in node.Attributes)
{
if (attribute.Name == "ID" && attribute.Value.StartsWith("AA"))
{
XmlAttribute newAttribute = newDoc.CreateAttribute(attribute.Name);
newAttribute.Value = attribute.Value;
newElement.Attributes.Append(newAttribute);
}
}
}
else
{
root.AppendChild(newDoc.ImportNode(node, true));
}
}

You can use Linq to XML:
var doc = XDocument.Load(#"yourfile.xml");
doc.Descendants("QWE")
.Where(x => !x.Attribute("ID").Value.StartsWith("AA"))
.Remove();
doc.Save("output.xml");

Related

Change Value of nested node

This seems like a simple question but I can't seem to get started on a working solution. The final goal is to change the value of the ConstantValue element highlighted below. My strategy is to find the Component node and drill down from there. The problem is that keep returning a null and I'm not sure why. Below is the code I'm using a the xml I'm using. Any hints would be great.
XDocument xmlDoc = XDocument.Parse(str);
var items = xmlDoc.Descendants("Component")
.Where(x => x.Attribute("Name").Value == "axesInterface")
.FirstOrDefault();
<?xml version="1.0" encoding="utf-8"?>
<Document>
<Engineering version="V17" />
<DocumentInfo>
</DocumentInfo>
<SW.Blocks.FB ID="0">
<AttributeList>
<Interface><Sections></Sections></Interface>
<MemoryLayout>Optimized</MemoryLayout>
<MemoryReserve>100</MemoryReserve>
<Name>EM00_CM01_Warp1</Name>
<Number>31650</Number>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
<SetENOAutomatically>false</SetENOAutomatically>
</AttributeList>
<ObjectList>
<SW.Blocks.CompileUnit ID="4" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource>
<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Access Scope="GlobalVariable" UId="27">
<Symbol>
<Component Name="HMIAxisCtrl_Interface" />
<Component Name="axesInterface" AccessModifier="Array">
<Access Scope="LiteralConstant">
<Constant>
<ConstantType>DInt</ConstantType>
<ConstantValue>0</ConstantValue>
</Constant>
</Access>
</Component>
</Symbol>
</Access>
</Parts>
</FlgNet>
</NetworkSource>
</AttributeList>
</SW.Blocks.CompileUnit>
</ObjectList>
</SW.Blocks.FB>
</Document>
You can use XQuery to get the correct node:
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
//
var nm = new XmlNamespaceManager(new NameTable());
nm.AddNamespace("sm", "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4");
var node = xdoc.XPathSelectElement(#"//sm:Access[#Scope=""LiteralConstant""]/sm:Constant/sm:ConstantValue", nm);
node.Value = "Something Else";
dotnetfiddle
For multiple nodes, change XPathSelectElement to XPathSelectElements
On your XML example, you can simply get specified node with this piece of code at any depth:
XDocument xmlDoc = XDocument.Parse(str);
XElement node = xmlDoc.Descendants().FirstOrDefault(x => x.Name.LocalName == "ConstantValue");
node.Value = "New value";
xmlDoc.Save(somewhere);
If there may be multiple nodes with "ConstantValue" name - then just replace FirstOrDefault with Where and work with filtered by names IEnumerable<XElement>.
Why it isn't find by x.Name == "ConstantValue":
EDIT: added samples.
// That's your parent node
var componentNode = xmlDoc.Descendants()
.Where(x => x.Name.LocalName == "Component"
&& (string)x.Attribute("Name") == "axesInterface");
// Get first ConstantValue node from parent Component node
var constantValueNode = componentNode.Descendants()
.FirstOrDefault(x => x.Name.LocalName == "ConstantValue");
// or get all ConstantValue nodes from parent Component node
var constantValueNodes = componentNode.Descendants()
.Where(x => x.Name.LocalName == "ConstantValue");
if (constantValueNode is XElement)
constantValueNode.Value = "New value";
You can create extension method to get parent node by specifying level:
public static class Extensions
{
public static XElement GetParentNode(this XElement element, int parentLevel)
{
XElement node = element.Parent;
for (int i = 0; i < parentLevel; i++)
node = node.Parent;
return node;
}
}
In your example, var parent = constantValueNode.GetParentNode(2); will return Component node (with attribute Name="axesInterface"). var parent = constantValueNode.GetParentNode(12); will return root "Document" node.

Linq to XML elements

Hi I try to read an xml file with LINQ.
The name of the file is:categorizedBooks.xml
and the content of the file looks like this:
<category name=".NET">
<books>
<book>CLR via C#</book>
<book>Essential .NET</book>
</books>
</category>
The code for reading the file looks like this:
XElement rootss = XElement.Load(#"D:/categorizedBooks.xml");
XElement dotNetCategoryss = rootss.Element("category");
XAttribute namehallo = dotNetCategoryss.Attribute("name");
XElement booksss = dotNetCategoryss.Element("books");
IEnumerable<XElement> bookElements = booksss.Elements("book");
Console.WriteLine((string)dotNetCategoryss);
foreach (XElement bookElement in bookElements)
{
Console.WriteLine(" - " + (string)bookElement);
}
But i get null on this line:
XAttribute namehallo = dotNetCategoryss.Attribute("name");
So how to fix this , so that it not will be null?
Thank you
Your doc root is the category element - try:
XAttribute namehallo = rootss.Attribute("name");
Console.WriteLine(namehallo.Value);
You'll have to change your other XElement references similarly
instead of XElement.Load use XDocument.Load or XDocument.Parse
var xml = #"
<category name='.NET'>
<books>
<book>CLR via C#</book>
<book>Essential .NET</book>
</books>
</category>";
var document = XDocument.Parse(xml);
// returns: .NET
var category = document
.Element("category")
.Attributes("name")
.Select(p => p.Value);
// returns: CLR via C#, Essential .NET
var books = document
.Descendants("book")
.Select(p => p.Value);

How to delete nodes from XML with exact string match in c#

I have below xml. I want to delete those nodes which doesn't contains attributes. in below xml I want to delete last 4 nodes which doesn't have attributes.
XML
<products>
<product system="kn-movies" code="UR">Unrated</product>
<product system="mu-movies" code="UR">Unrated</product>
<product system="na-movies" code="UR">Unrated</product>
<product system="fj-movies" code="UR">Unrated</product>
<product>Unrated (Unrated )</product>
<product>Unrated (Unrated )</product>
<product>Unrated (Без классификации )</product>
<product>Unrated (غير مصنف )</product>
</products>
I had tried this c# code
var ratingNode = document.Descendants("rating").Where(t => t != null && t.Equals(Convert.ToString(nodeItem))).FirstOrDefault();
if (ratingNode != null)
{
ratingNode.Remove();
}
but it doesn't worked for me. please help me out where i m doing mistakes.
System.Xml.Linq.XDocument doc = System.Xml.Linq.XDocument.Load(filepath);
var newList = doc.Root.Descendants().ToList().RemoveAll(x => x.Attributes() == null || x.Attributes().Count() <= 0);
Hope it helps!!
Hope this will do trick
XmlNodeList nodes = xmlDocument.GetElementsByTagName("products");
foreach(XmlNode node in nodes)
{
if(node.Attributes.Count == 0)
{
node.RemoveAll;
}
}
You could use Linq to Xml and query for elements which has no attributes, then you just need Remove call to remove those elements.
XDocument doc = XDocument.Load(filepath);
doc.Root
.Descendants() // flattens the structure
.Where(x=> x.Attributes().Count() <= 0) // filter elements which has no attributes
.Remove(); // Remove them
Check this Demo

What is the XPath for this XML with a Namespace

I am new to XPath. I need help with the XPath needed to extract the Book's Title and Authors towards the end of this XML. I tried the following C# code with no success. I just need to list the Book's Title and Authors under . Looks like the xmlns namespace affects my XPaths. My code works if I manually remove the xmlns. So, either I modify the XPath to account for this namespace or figure out a way to remove that attribute from the XML. Please advise.
Here is the C# code:
XmlNodeList nodes = XML.DocumentElement.SelectNodes("//Title");
foreach (XmlNode node in nodes)
{
Console.WriteLn(node.Name + " = " + node.InnerText); }
}
Here is the XML:
<?xml version="1.0"?>
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<RequestId>xxxxx</RequestId>
<Arguments>
<Argument Name="Condition" Value="All"></Argument>
<Argument Name="ResponseGroup" Value="Small,Images"></Argument>
<Argument Name="SearchIndex" Value="Books"></Argument>
</Arguments>
<RequestProcessingTime>0.0735170000000000</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<IsValid>True</IsValid>
<ItemSearchRequest>
<Condition>All</Condition>
<Keywords>Perl</Keywords>
<ResponseGroup>Small</ResponseGroup>
<ResponseGroup>Images</ResponseGroup>
<SearchIndex>Books</SearchIndex>
</ItemSearchRequest>
</Request>
<TotalResults>3761</TotalResults>
<TotalPages>377</TotalPages>
<MoreSearchResultsUrl>http://www.amazon.com/gp/redirect.html?camp=2025&creative=386001&location=http%3A%2F%2Fwww.amazon.com%2Fgp%2Fsearch%3Fkeywords%3DPerl%26url%3Dsearch-alias%253Dstripbooks&linkCode=xm2&tag=geo01d-20&SubscriptionId=AKIAJJBQEKP2X72RQ6XA</MoreSearchResultsUrl>
<Item>
<ASIN>1449303587</ASIN>
<DetailPageURL>http://www.amazon.com/Learning-Perl-Randal-L-Schwartz/dp/1449303587%3FSubscriptionId%3DAKIAJJBQEKP2X72RQ6XA%26tag%3Dgeo01d-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D1449303587</DetailPageURL>
<ItemLinks>
<ItemLink>
<Description>Technical Details</Description>
<URL>http://www.amazon.com/Learning-Perl-Randal-L-Schwartz/dp/tech-data/1449303587%3FSubscriptionId%3DAKIAJJBQEKP2X72RQ6XA%26tag%3Dgeo01d-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D1449303587</URL>
</ItemLink>
<ItemLink>
<Description>All Offers</Description>
<URL>http://www.amazon.com/gp/offer-listing/1449303587%3FSubscriptionId%3DAKIAJJBQEKP2X72RQ6XA%26tag%3Dgeo01d-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D1449303587</URL>
</ItemLink>
</ItemLinks>
<SmallImage>
<URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL75_.jpg</URL>
<Height Units="pixels">75</Height>
<Width Units="pixels">58</Width>
</SmallImage>
<MediumImage>
<URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL160_.jpg</URL>
<Height Units="pixels">160</Height>
<Width Units="pixels">123</Width>
</MediumImage>
<LargeImage>
<URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL.jpg</URL>
<Height Units="pixels">500</Height>
<Width Units="pixels">385</Width>
</LargeImage>
<ImageSets>
<ImageSet Category="primary">
<SwatchImage>
<URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL30_.jpg</URL>
<Height Units="pixels">30</Height>
<Width Units="pixels">23</Width>
</SwatchImage>
<SmallImage>
<URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL75_.jpg</URL>
<Height Units="pixels">75</Height>
<Width Units="pixels">58</Width>
</SmallImage>
</ImageSet>
</ImageSets>
<ItemAttributes>
<Author>Randal L. Schwartz</Author>
<Author>brian d foy</Author>
<Author>Tom Phoenix</Author>
<Manufacturer>O'Reilly Media</Manufacturer>
<ProductGroup>Book</ProductGroup>
<Title>Learning Perl</Title>
</ItemAttributes>
</Item>
</Items>
</ItemSearchResponse>
You can do this with Linq-to-XML:
XDocument xmlDoc = XDocument.Parse(xmlString);
var q = from el in xmlDoc.Descendants()
.Where(x => x.Name.LocalName == "Title" || x.Name.LocalName == "Author")
select el;
foreach (var xElement in q)
{
Debug.WriteLine(xElement.Name.LocalName + " : " + xElement.Value);
}
The Output:
Author : Randal L. Schwartz
Author : brian d foy
Author : Tom Phoenix
Title : Learning Perl
The XPath needs to be "//p:Title", and you need to tell the XPath processor that the namespace prefix "p" stands for the namespace "http://webservices.amazon.com/AWSECommerceService/2011-08-01". The way you establish namespace bindings depends on the API of your chosen XPath processor. For C# you can find an explanation of how to do it here (or in many, many other previous StackOverflow answers):
Xml-SelectNodes with default-namespace via XmlNamespaceManager not working as expected
I finally figured out an answer to my question. I simply removed the namespace which overly complicated my XPath's. The C# code below works. PrintKeyValue simply prints Key = Value.
XML = new XmlDocument();
using (XmlTextReader reader = new XmlTextReader("C:\\Path\\File.xml"))
{
reader.Namespaces = false;
XML.Load(reader);
}
XmlNodeList items = XML.DocumentElement.SelectNodes("//Item");
foreach (XmlNode item in items)
{
PrintKeyValue("ISBN10", item["ASIN"].InnerText);
XmlNode attrib = item.SelectSingleNode(".//ItemAttributes");
PrintKeyValue("Title", attrib["Title"].InnerText);
XmlNodeList authors = attrib.SelectNodes(".//Author");
StringBuilder sb = new StringBuilder();
int count = 0;
foreach (XmlNode author in authors)
{
count++;
if (count > 1) { sb.Append(", "); }
sb.Append(author.InnerText);
}
PrintKeyValue("Authors", sb.ToString());
}

how to check xmlnode for innertext or value in c#?

I created xmlnodelist and i want to handle the value of elements that dont have childs.
at the following code i'm checking for childnodes and i get true from all of the elements, even
those without childs.
how can i pick the last elements in the tree and handle the value's?
XmlDocument XDoc = new XmlDocument();
XDoc.Load("d://avi.xml");
XmlNodeList XList = XDoc.SelectNodes("//*");
foreach (XmlElement XNode in XList)
{
if (XNode.HasChildNodes == true)
{
Console.WriteLine("this node has childs");
continue;
}
else Console.WriteLine("this node dont have childs");
}
<level1>
<level2>
<level3>header3</level3>
<level4>another</level4>
<level31>header31</level31>
</level2>
<level2>
<level3>111</level3>
<level31>nn</level31>
</level2>
</level1>
How about using Linq to Xml for this?
var xElem = XElement.Parse(xml);
var leafElements = xElem.Descendants()
.Where(e => !e.HasElements)
.ToList();
The text within an element is a "node" as well. What you want is
if (XNode.ChildNodes.Any(n=>n.NodeType == XmlNodeType.Element))
Alternatively you can loop through the ChildNodes and see if one of them is an element.

Categories