Remove all Artist node from an XML file - c#

I have an XmlWriter that contains a xml that looks like the one below, just with a lot more nodes. what's the fastest and best way to remove all the ARTIST node from this xml ?
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
</CD>
</CATALOG>

As long as the file isn't gigabytes XmlDocument should be fine:
XmlDocument XDoc = new XmlDocument();
XDoc.Load(MapPath(#"~\xml\test.xml"));
XmlNodeList Nodes = XDoc.GetElementsByTagName("ARTIST");
foreach (XmlNode Node in Nodes)
Node.ParentNode.RemoveChild(Node);
XDoc.Save(MapPath(#"~\xml\test_out.xml"));

When I tried Steve's solution I received the following error "The element list has changed. The enumeration operation failed to continue". I know it is a bit of a hack but to get around this I used the following:
//Load XML
XmlDocument XDoc = new XmlDocument();
XDoc.Load(MapPath(#"~\xml\test.xml"));
//Get list of offending nodes
XmlNodeList Nodes = XDoc.GetElementsByTagName("ARTIST");
//Loop through the list
while (Nodes.Count != 0) {
foreach (XmlNode Node in Nodes) {
//Remove the offending node
Node.ParentNode.RemoveChild(Node); //<--This line messes with our iteration and forces us to get a new list after each remove
//Stop the loop
break;
}
//Get a refreshed list of offending nodes
Nodes = XDoc.GetElementsByTagName("ARTIST");
}
//Save the document
XDoc.Save(newfile);
I was in a bind and needing something quick and this got the job done.

From the way your question is worded, I'm assuming that you have an XmlWriter that has been used to write an XmlDocument and you want to manipulate what it is going to write before flush is called.
I'm afraid that what you're trying to do may be impossible. The XmlWriter is not meant to manipulate XML before it is written to a destination stream.

Related

Modfiying Xml using C#

I have an xml
<PolicyHdr>
<PolicyHdrData>
<ID>123</ID>
</PolicyHdrData>
</PolicyHdr>
<Role>
<RoleData>
<DisplayFieldName>Payor</DisplayFieldName>
</RoleData>
</Role>
<PolicyCovHdr>
<PolicyCovHdrData>
<ID>000</ID>
<PolicyHdrID>123</PolicyHdrID>
</PolicyCovHdrData>
</PolicyCovHdr>
<Role>
<RoleData>
<DisplayFieldName>Insured</DisplayFieldName>
</RoleData>
</Role>
I need to modify this xml such that the PolicyCovHdr comes as child node of PolicyHDr where the ID node under PolicyHdr is equal to the PolicyHdrID under POlicyCovHDr.
Also the Role Nodes , whenever the DisplayFieldName is Payor it should be child of PolicyHdr and of the DisplayFieldName is Insure it should be the child node of PolicyCovHdr
So the final xml should look something like this:-
<PolicyHdr>
<PolicyHdrData>
<ID>123</ID>
<Role>
<RoleData>
<DisplayFieldName>Payor</DisplayFieldName>
</RoleData>
</Role>
</PolicyHdrData>
<PolicyCovHdr>
<PolicyCovHdrData>
<ID>000</ID>
<PolicyHdrID>123</PolicyHdrID>
<Role>
<RoleData>
<DisplayFieldName>Insured</DisplayFieldName>
</RoleData>
</Role>
</PolicyCovHdrData>
</PolicyCovHdr>
</PolicyHdr>
Could someone help me with some logic which I could use to achieve this xml
Hi sorry I forgot to add my code changes here
string xml1 = "<xml><PolicyHdr><PolicyHdrData><ID>123</ID></PolicyHdrData></PolicyHdr><Role><RoleData><DisplayFieldName>Payor</DisplayFieldName></RoleData></Role><PolicyCovHdr><PolicyCovHdrData><ID>000</ID><PolicyHdrID>123</PolicyHdrID></PolicyCovHdrData></PolicyCovHdr><Role><RoleData><DisplayFieldName>Insured</DisplayFieldName></RoleData></Role></xml>";
XmlDocument test = new XmlDocument();
test.LoadXml(xml1);
XmlNodeList polList = test.SelectNodes("//PolicyHdr/PolicyHdrData");
foreach (XmlNode xn in polList)
{
XmlNode polNode = xn.SelectSingleNode("//PolicyHdr/PolicyHdrData/ID");
XmlNodeList polCovNodeList = test.SelectNodes("//PolicyCovHdr/PolicyCovHdrData");
foreach (XmlNode yn in polCovNodeList)
{
XmlNode polCovPolNode = yn.SelectSingleNode("//PolicyCovHdr/PolicyCovHdrData/PolicyHdrID");
if(polNode.InnerText.ToString().Equals(polCovPolNode.InnerText.ToString()))
{
xn.AppendChild(yn);
}
}
XmlNodeList polRoleList = test.SelectNodes("//Role/RoleData");
foreach (XmlNode zn in polRoleList )
{
XmlNode RoleNode = zn.SelectSingleNode("//Role/RoleData/DisplayFieldName");
if(RoleNode.InnerText.ToString().Equals("Payor"))
{
xn.AppendChild(zn);
}
//Console.WriteLine(RoleNode.InnerText.ToString());
}
Console.WriteLine(xn.OuterXml.ToString());
}
The problem right now I am facing is the complexity is (n^2) and adding the Role node as child to PolicyCovHdr when DisplayFieldName = Insured.
Right now the output looks like this
<PolicyHdrData><ID>123</ID><PolicyCovHdrData><ID>000</ID><PolicyHdrID>123</PolicyHdrID></PolicyCovHdrData><RoleData><DisplayFieldName>Payor</DisplayFieldName></RoleData></PolicyHdrData>
I am still thinking of how to add the Role with DisplayFieldname as Insured as well because of the complexity is already very high
I think that you should use XML Serialization/Deserialization. You can look for example to this question: C# Xml Serialization & Deserialization
At first you can create an object with structure of your input XML, than use XML Deserializer to load the data from XML to this object. After that you can work with the instance of an object and ad some conditions to your code which you need.
You can create another objects which will be representing output XML (depends on the structure as you wrote about it) and in if/else code filling the object with the data you need. Structure of XML will be represented by the structure of the object. Finally you can use XML Serializer to create your XML.
There could probably be better solution, but that should also work.

Add multiple child nodes to xml using xpath

I have a xml like below
<ProcessInvoice>
<ApplicationArea>
<CreationDateTime>2016-06-01 13:15:36</CreationDateTime>
<ApplicationGroup>BBEX</ApplicationGroup>
<MessageType>PROCESSINVOICE</MessageType>
</ApplicationArea>
</ProcessInvoice>
Now I have path and value to add, but it is dynamic.
It can be like following
path-/ProcessInvoice/ApplicationArea/UserArea/Sample1
value-001
path-/ProcessInvoice/ApplicationArea/UserArea/UserAreaLine/Sample1
value-002
if the path is present then i have to add the value, else modify the value.
I can split the path and loop through to find till what node is present and what i have to add but I think there might be more elegant way of doing this.Please help me with the best approach to solve this?
Edit
Note- I will prefer XDocument And XElement.
May be I didnt explain properly.
My xml and node path both are dynamic.
There might be situation where multiple nodes are missing from my xml.
Now problem is i need to identify upto which node is existing in xml and which nodes i need to create.
Thanks
If "Sample1" node always exist, the code will look like this:
XmlDocument doc = new XmlDocument();
doc.Load(FILE);
var userArea = DocumentElement["ProcessInvoice"]["ApplicationArea"]["UserArea"];
foreach (XmlNode element in userArea.ChildNodes)
{
if (element.Name== "Sample1" )
{
XmlNode node == element;
node.InnerText ="001";
}
else if (element.Name == "UserAreaLine")
{
XmlNode node == element["Sample1"];
node.InnerText ="002";
}
}

Renaming Xml node

I try to rename any node starts with "NP" to "NP" in an XmlDocument. I only found this related questoin. based on the most up-voted answer I wrote:
XmlNodeList npNodeList = PSbank.SelectNodes("//*[starts-with(name(), 'NP')]");
foreach (XmlNode npNode in npNodeList)
{
// create new (renamed) Content node
XmlNode newNode = PSbank.CreateElement("NP");
// [if needed] copy existing Content children
newNode.InnerXml = npNode.InnerXml;
// replace existing Content node with newly renamed Content node
npNode.ParentNode.InsertBefore(newNode, npNode);
npNode.ParentNode.RemoveChild(npNode);
}
However, it seems it doesn't work in my case. When an NPX node contains other NPX nodes, the inner nodes are not replaced because the code manipulates XmlDocument and replaces the outer node with a new created node that contains the old innerXml.
Example Xml:
<S>
<NPC>
<NP> A </NP>
<NPA> B </NPA>
</NPC>
<NPD>
C
</NPD>
</S>
I look for a more general and elegant answer for such a problem.
I'd do this with LINQ to XML, it's a lot easier and cleaner. Firstly, it's generally a much nicer library, and secondly: XElement.Name is mutable.
var doc = XDocument.Parse(xml); // or use XDocument.Load
foreach (var element in doc.Descendants())
{
if (element.Name.LocalName.StartsWith("NP"))
{
element.Name = "NP";
}
}
See this fiddle for a working demo.

Selecting Particular Node List in XML

<Report xmlns="Microsoft.SystemCenter.DataWarehouse.Report.Alert" xmlns:p1="w3.org/2001/XMLSchema-instance"; Name="Microsoft.SystemCenter.DataWarehouse.Report.Alert" p1:schemaLocation="Microsoft.SystemCenter.DataWarehou?Schema=True">
<Title>Alert Report</Title>
<Created>6/27/2013 9:32 PM</Created>
<StartDate>6/1/2013 9:29 PM</StartDate>
<EndDate>6/27/2013 9:29 PM</EndDate>
<TimeZone>(UTC)</TimeZone>
<Severity>Warning, Critical</Severity>
<Priority>Low, Medium, High</Priority>
<AlertTable>
<Alerts>
<Alert>
<AlertName></AlertName>
<Priority></Priority>
</Alert>
</Alerts>
</AlertTable>
</Report>
So I'm trying to pull down the list of nodes that appear under Alerts child. So /Report/AlertTable/Alerts.
I've done very similar before but in this format it is not working for some reason. Can someone point me out in the right direction?
XmlDocument Log = new XmlDocument();
Log.Load("test.xml");
XmlNodeList myLog = Log.DocumentElement.SelectNodes("//Report/AlertTable/Alerts");
foreach (XmlNode alert in myLog)
{
Console.Write("HERE");
Console.WriteLine(alert.SelectNodes("AlertName").ToString());
Console.WriteLine(alert.SelectNodes("Priority").ToString());
Console.Read();
}
EDIT:
One of the responses had me try to use a bunch of namespace with p1 but had no such luck.
EDIT:
Did not work either:
var name = new XmlNamespaceManager(log.NameTable);
name.AddNamespace("Report", "http://www.w3.org/2001/XMLSchema-instance");
XmlNodeList xml = log.SelectNodes("//Report:Alerts", name);
From a site:
nodename Selects all nodes with the name "nodename"
/ Selects from the root node
// Selects nodes in the document from the current node that match the selection no matter where they are
So I believe
"/AlertTable/Alerts"
would work, as that would be 'from the root node' as well as
"Report/AlertTable/Alerts"
XPath Site
Figured this sucker out.
It had to do with the namespace of "Microsoft.SystemCenter.DataWarehouse.Report.Alert". Changing this to anything but that won't read the XML properly.
XmlDocument log = new XmlDocument();
log.Load(#"C:\Users\barranca\Desktop\test.xml");
// XmlNodeList xml = log.SelectNodes("//ns1:Alerts");
var name = new XmlNamespaceManager(log.NameTable);
name.AddNamespace("ns1", "Microsoft.SystemCenter.DataWarehouse.Report.Alert");
XmlNodeList xml = log.SelectNodes("//ns1:Alert", name);
foreach (XmlNode alert in xml)
{
Console.Write("HERE");
XmlNode test = alert.SelectSingleNode("//ns1:AlertName",name);
string testing = test.InnerText;
Console.Write(testing);
}

Read first root node from XML

I work with three kinds of XML files :
Type A:
<?xml version="1.0" encoding="UTF-8"?>
<nfeProc versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
</nfeProc>
Tyepe B:
<?xml version="1.0" encoding="UTF-8"?>
<cancCTe xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
</cancCTe>
Type C:]
<?xml version="1.0" encoding="UTF-8"?>
<cteProc xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
</cteProc>
I have try with this code to read the first node :
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
XmlNodeList ml = xmlDoc.GetElementsByTagName("*");
XmlElement root = xmlDoc.DocumentElement;
exti = root.ToString();
but dont return anything i want to read the first node , need to know if the file is nfeProc ,canCTE or cteProc
The second question is how i get the value from "value" in the same tag???
Thanks
From this post:
//Root node is the DocumentElement property of XmlDocument
XmlElement root = xmlDoc.DocumentElement
//If you only have the node, you can get the root node by
XmlElement root = xmlNode.OwnerDocument.DocumentElement
I would suggest using XPath. Here's an example where I read in the XML content from a locally stored string and select whatever the first node under the root is:
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(xml));
XmlNode node = doc.SelectSingleNode("(/*)");
If you aren't required to use the XmlDocument stuff, then Linq is your friend.
XDocument doc = XDocument.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
XElement first = doc.GetDescendants().FirstOrDefault();
if(first != null)
{
//first.Name will be either nfeProc, canCTE or cteProc.
}
Working with Linq to XML is the newest and most powerful way of working with XML in .NET and offers you a lot more power and flexibility than things like XmlDocument and XmlNode.
Getting the root node is very simple:
XDocument doc = XDocument.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
Console.WriteLine(doc.Root.Name.ToString());
Once you have constructed an XDocument you don't need to use any LINQ querying or special checking. You simply pull the Root property from the XDocument.
Thanks i have solved this way the first part
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(nomear);
XmlNodeList ml = xmlDoc.GetElementsByTagName("*");
XmlNode primer = xmlDoc.DocumentElement;
exti = primer.Name;
First, to be clear, you're asking about the root element, not the root node.
You can use an XmlReader to avoid having to load large documents completely into memory. See my answer to a how to find the root element at https://stackoverflow.com/a/60642354/1307074.
Second, once the reader is referencing the element, you can use the reader's Name property to get the qualified tag name of the element. You can get the value as a string using the Value property.

Categories

Resources