Remove elements with named child element - c#

I have XML where I remove tags using this code:
XElement xmlElement = XElement.Parse(xml);
xmlElement.XPathSelectElement("//"+ tagToRemove).Remove();
But now I have another problem. On the method above I remove a specific tag but now I want to remove the tag below. In the XML there is a lot of tag called param so I can't just remove that.
What I was thinking about was to remove the tag where the subtag
name = ERP_OUTPUT_400_20_DOCUMENTATIONLANGUAGE_field
<param type=\"read-only\" committed=\"no\" changed=\"no\" visible=\"yes\" no=\"1\">
<name>ERP_OUTPUT_400_20_DOCUMENTATIONLANGUAGE_field</name>
<desc>ERP OUTPUT 400 20 DOCUMENTATIONLANGUAGE</desc>
<val>SV;</val>
<val-desc>SV;</val-desc>
<domain name=\"function\" />
<aux>
<prop name=\"hidden\">no</prop>
</aux>
</param>
How can I do that?

To select element having child element <name> value equals specific name, you can use the following XPath query :
//element_name[name='specific_name']
Example based on your existing code :
var name = "ERP_OUTPUT_400_20_DOCUMENTATIONLANGUAGE_field";
var xpath = String.Format("//{0}[name='{1}']", tagToRemove, name);
xmlElement.XPathSelectElement(xpath).Remove();

Related

C# Create XmlElement from string without xmlns=""

I am building an xml document and I have declared the namespace at the very top.
<Root xmlns="http://www.omg.org/space/xtce" xmlns:xtce="http://www.omg.org/space/xtce" ...>
At some arbitrary level below I want to AppendChild with an element created from a string. My goal is to end up with a document that contains that element without the xmlns AT ALL.
This is the closest I have gotten-
string someElementStr = "<SomeElement name="foo"><SubElement name="bar" /></SomeElement>";
XmlDocumentFragment node = doc.CreateDocumentFragment();
node.InnerXml = someElementStr;
someXmlNodeWithinDoc.AppendChild(node);
This code results in-
<SomeElement name="foo" xmlns="">
<SubElement name="bar" />
</SomeElement>
in the final document.
I use a different construct when I do not have to go from a string to XML-
XmlElement elem = doc.CreateElement("SomeElement", "http://www.omg.org/space/xtce");
elem.SetAttribute("name","foo");
someXmlNodeWithinDoc.AppendChild(elem);
and this yields exactly what I want.
<SomeElement name="foo">
</SomeElement>
I would like to do something line this in my current solution
node.setNamespace("http://www.omg.org/space/xtce") then the document would omit xmlns because it is same as root.
Can someone tell me the idiomatic way to build a document with a single namespace use within, where some elements are stored in the model as a string?
This issue is almost identical to mine except the solution has the luxury of only providing the sub element as a string (everything under "new"). I need the entire element.
string xmlRoot = "<Root xmlns=\"http://www.omg.org/space/xtce\"></Root>";
string xmlChild = "<SomeElement name=\"foo\"><SubElement name = \"bar\"/></SomeElement >";
XDocument xDoc = XDocument.Parse(xmlRoot);
XDocument xChild = XDocument.Parse(xmlChild);
xChild.Root.Name = xDoc.Root.GetDefaultNamespace() + xChild.Root.Name.LocalName;
foreach (XElement xChild2 in xChild.Root.Nodes())
{
xChild2.Name = xDoc.Root.GetDefaultNamespace() + xChild2.Name.LocalName;
}
xDoc.Root.Add(xChild.Root);
string xmlFinal = xDoc.ToString();
This is the solution I ended up with. I did not use #shop350 solution because I didn't want to use XDocument,XElement... Thank you for the feedback though!
// create a fragment which I am going to build my element based on text.
XmlDocumentFragment frag = doc.CreateDocumentFragment();
// here I wrap my desired element in another element "dc" for don't care that has the namespace declaration.
string str = "";
str = "<dc xmlns=\"" + xtceNamespace + "\" ><Parameter name=\"paramA\" parameterTypeRef=\"paramAType\"><AliasSet><Alias nameSpace=\"ID\" alias=\"0001\"/></AliasSet></Parameter></dc>";
// set the xml for the fragment (same namespace as doc)
frag.InnerXml = str;
// let someXmlNodeWithinDoc be of type XmlNode which I determined based on XPath search.
// Here I attach the child element "Parameter" to the XmlNode directly effectively dropping the element <dc>
someXmlNodeWithinDoc.AppendChild(frag.FirstChild.FirstChild);

Linq to XML to retrieve value based on Attribute

I have a Linq to Xml query that needs to retrieve a value based on the attribute value of a particular node. I'm trying to retrieve a list of items and one of the nodes has an attribute that I can't seem to find a way to get the value.
Here's the XML:
<codelist-items>
<codelist-item>
<code>1</code>
<name>
<narrative>Planned start</narrative>
<narrative xml:lang="fr">Début prévu</narrative>
</name>
<description>
<narrative>
The date on which the activity is planned to start, for example the date of the first planned disbursement or when physical activity starts.
</narrative>
</description>
</codelist-item>
<codelist-item>
<code>2</code>
<name>
<narrative>Actual start</narrative>
<narrative xml:lang="fr">Début réel</narrative>
</name>
<description>
<narrative>
The actual date the activity starts, for example the date of the first disbursement or when physical activity starts.
</narrative>
</description>
</codelist-item>
</codelist-items>
I'm only displaying 2 items to keep it short. And here is my Linq query to try and retrieve the value from "name/narrative" where there is a "xml:lang='fr'" attribute:
XElement xelement = XElement.Load(xmlFile);
var elements = from adt in xelement.Elements("codelist-items").Elements("codelist-item")
select new ActivityDateType
{
Code = (string)adt.Element("code"),
NameEng = (string)adt.Element("name").Element("narrative"),
NameFra = (string)adt.Element("name").Element("narrative[#xml:lang='fr']"),
Description = (string)adt.Element("description")
};
return elements;
Anyone know how to get the value for NameFra?
Thanks
You can either use LINQ FirstOrDefault() with predicate that filters the element by its attribute value :
NameFra = (string)adt.Element("name")
.Elements("narrative")
.FirstOrDefault(o => (string)o.Attribute(XNamespace.Xml+"lang") == "fr"),
Or use XPathSelectElement() extension to execute your XPath expression which already contains attribute filtering logic :
NameFra = (string)adt.Element("name")
.XPathSelectElement("narrative[#xml:lang='fr']", nsmgr),
The latter can be simplified further to the following :
NameFra = (string)adt.XPathSelectElement("name/narrative[#xml:lang='fr']", nsmgr),
nsmgr assumed has previously been declared as follow :
var nsmgr = new XmlNamespaceManager(new NameTable());
nsmgr was needed because the XPath contains prefix xml (XPathSelectElement() complained when I use the overload which accepts just XPath string argument without namespace manager).

How to make a copy of a XML node with all their child nodes and values but different name C# .NET

I'm trying to make a copy of a XML node and all their child nodes but different XML parent node name, but is throwing me an error, this is the xml file:
<Servers>
<MyServer>
<Host>0.0.0.0</Host>
<Port>12</Port>
<User>USER</User>
</MyServer>
</Servers>
What I'm trying to do is a copy of MyServer with all their child nodes and values but different name... something like this
<Servers>
<MyServer>
<Host>0.0.0.0</Host>
<Port>12</Port>
<User>USER</User>
</MyServer>
<MyCopyofMyServer>
<Host>0.0.0.0</Host>
<Port>12</Port>
<User>USER</User>
</MyCopyofMyServer>
</Servers>
What I did was this:
public void CopyInterface(string NewServer, string ServerToCopy)
{
xmldoc.Load(XMLInterfacesFile);
XmlNode NodeToCopy = xmldoc.SelectSingleNode("Servers/" + ServerToCopy);
XmlNode deep = NodeToCopy.CloneNode(true);
deep.InnerXml = deep.InnerXml.Replace(ServerToCopy, NewServer);
xmldoc.AppendChild(deep); //Throwing an exception here!
xmldoc.Save(XMLInterfacesFile);
}
Exception: This document already has a 'DocumentElement' node.
Any Idea?
The line
xmldoc.AppendChild(deep);
tries to append an element to an XmlDocument. This means that it is trying to add a root level element. The issue is that your document already has root level element (Servers) and it cannot add another one, so you get an exception.
Another issue with your code is that in line
deep.InnerXml = deep.InnerXml.Replace(ServerToCopy, NewServer);
you are attempting to replace the name of the server with the new name. Unfortunatelly InnerXml looks like this:
<Host>0.0.0.0</Host>
<Port>12</Port>
<User>USER</User>
so your server name is never replaced.
To fix the issues you can try different approach:
// Fint the node you want to replace
XmlNode NodeToCopy = xmldoc.SelectSingleNode("Servers/" + ServerToCopy);
// Create a new node with the name of your new server
XmlNode newNode = xmldoc.CreateElement(NewServer);
// set the inner xml of a new node to inner xml of original node
newNode.InnerXml = NodeToCopy.InnerXml;
// append new node to DocumentElement, not XmlDocument
xmldoc.DocumentElement.AppendChild(newNode);
This should give you the result you need

c#: get the attribute's xpath for an xml element

Given the following XML:
<enrollment>
<school>
<students>
<studentA fname="John" lname="Doe" age="23" />
<studentB fname="Mary" lname="Johnson" age="22" />
</students>
</school>
</enrollment>
and here's my code to iterate the attributes-
foreach(XmlAttribute attr in node.Attributes)
{
//--get the XPath for each attribute
}
Where node = "studentA", how do I get the XPath for each attribute?
EDIT:
Basically what I'm trying to achieve here is to compare if two nodes are the same. So I have to check if they have the same name, attributes, and attribute values. Therefore given a node, I need an xpath expression that that matches the conditions stated.
you can directly put it like as for all the studentA nodes-
Xpath- "//studentA"
or to iterate over a specific node-
Xpath- "enrollment/school/students/studentA"
In case you want to find attribute fname
Xpath- "enrollment/school/students/studentA[#fname]"
Assuming myXml is your xmlDocument
you can iterate over a particular node attributes like-
System.Xml.XmlNode xn = myXml.SelectSingleNode("enrollment/school/students/studentA");
foreach (System.Xml.XmlAttribute attrib in xn.Attributes)
{
// find attribute name using attrib.Name
string sAttribName = attrib.Name;
if (sAttribName == "fname")
{
//Check your codes here
}
}
You can use enrollment/school/students/studentA/#fname
#attribute is for attribute selection.

object reference not set to an instance of an object xml C# and adding some node in xml file using C#

this is simple code for using xml in C#. I want to add data like
<table1 name="something">
<column1 someattribute="something"> actualname </column>
</table1>
then I want to add this element into XDocument's Object
XDocument document;
public Form1()
{
InitializeComponent();
document = new XDocument();
}
private void button1_Click(object sender, EventArgs e)
{
//document = new XDocument();
XElement elem = new XElement("table1");
elem.Add(new XAttribute("TableName", textBox1.Text));
elem.Add(new XElement("Column1",new XAttribute("Someattribute", somevalue));
document.Element("table1").Add(elem);//throws exception object refernce not set ..."
}
I have tried the following code Adding elements to an xml file in C# but it throws an exception of "object reference not set to an instance of an object xml C#"
Please help
P.S: suppose there are many columns in table1 then first I want to accmulate each column then add it to xdocument and then there is table2 and I want to do the same thing with him.
There are no table1 elements in your XML structure. This means that your document.Element("table1") table returns null, and therefore there is an exception when you call .Add(elem) on it.
Debugging NullReferenceExceptions is very basic most of the time and these problems can easily be solved simply by stepping through the code with a debugger.
For reference, the Element method
Gets the first (in document order) child element with the specified XName.
Which is why you get a null. (MSDN)
When you initialize the form, you create an empty document like so new XDocument(). Then, in your button click, you are trying to add a new element as a child to table1 by using the selector document.Element("table1"). That is the issue, your XDocument is empty! You would need to first create a table1 element or instead add your elem object directly to the document.
Meaning you can either do this to make sure table1 exists:
document = XDocument.Parse("<table1></table1>);
or change your click method to add directory to the root of the document:
private void button1_Click(object sender, EventArgs e)
{
//document = new XDocument();
XElement elem = new XElement("table1");
elem.Add(new XAttribute("TableName", textBox1.Text));
elem.Add(new XElement("Column1",new XAttribute("Someattribute", somevalue)));
document.Add(elem);
}
Every XML document/structure has to have one unique node/tag, called the root node, that should exist only once in every document. So a proper xml can look like this - one root, but many nodes:
<?xml version="1.0" encoding="UTF-8"?>
<root name="example">
<node attribute="1">information 1</node>
<node attribute="2">information 2</node>
</root>
There are two possibilities to access the root element using LINQ2XML: XDocument.Root and XDocument.Element("elementName").
Knowing this and using the given xml code, it should be easy to follow the following example, that uses LINQ2XML to manipulate the xml code:
// every XML structure has to have a root node, that is unique
// and should exists only once!
var xmlCode = #"<root name=""example"">
<node attribute=""1"">information 1</node>
<node attribute=""2"">information 2</node>
</root>";
// load the example xml code
var document = XDocument.Parse(xmlCode);
// create new attribute object
var newAttribute = new XAttribute("attribute", "3");
// create new node / column object with the newly created attribute and set the content of the node
var newNode = new XElement("node", newAttribute, "information 3");
// add the newly created node to the XML Document root
// in LINQ2XML the root node can be accessed via the Root property
// of an XDocument object
document.Root.Add(newNode);
// alternative: find the document root node using its name, in this example the node
// is named "root" and add the new element to it!
// document.Element("root").Add(newNode);
The output is:
<root name="example">
<node attribute="1">information 1</node>
<node attribute="2">information 2</node>
<node attribute="3">information 3</node>
</root>
In your question table1 is root in this xml code and column1 equivalent is node.
If you are going to use LINQ2XML, than have a look at all methods that the framework offers.
Throw exception, because at the time the item is inserted in the XDocument(document) XElement (elem) element Element("Table") does not exist yet
You are trying to add your now element to an existing element that doesn't exist:
document.Element("table1").Add(elem);//throws exception object refernce not set ..."
The table1 element hasn't been added to the document yet. You need to reference the appropriate parent element. To add it to the root element use
document.Root.Add(elem);

Categories