Add XML element before last element using Linq - c#

I need to add new element before the last element of the XML using Linq.
For example:
<?xml version="1.0" encoding="utf-8"?>
<products>
<product id="p1">
<name>Delta</name>
<price>800</price>
<country>Denmark</country>
</product>
<product id="p3">
<name>Alfa</name>
<price>1200</price>
<country>Germany</country>
</product>
</products>
New element <stock></stock> should be inserted before <country> element using Linq

// load the XML file into XDocument instance
var doc = XDocument.Load("sourceFile.xml");
// find all <country> elements
var countries = doc.Root.Elements("product").Elements("Country");
// add <stock /> before each <country> elements
foreach(var country in countries)
{
country.AddBeforeSelf(new XElement("stock"));
}
// save document to the file
doc.Save("sourceFile.xml");

Related

Unable to parse xml string using Xdocument and Linq

I would like to parse the below xml using XDocument in Linq.
<?xml version="1.0" encoding="UTF-8"?>
<string xmlns="http://tempuri.org/">
<Sources>
<Item>
<Id>1</Id>
<Name>John</Name>
</Item>
<Item>
<Id>2</Id>
<Name>Max</Name>
</Item>
<Item>
<Id>3</Id>
<Name>Ricky</Name>
</Item>
</Sources>
</string>
My parsing code is :
var xDoc = XDocument.Parse(xmlString);
var xElements = xDoc.Element("Sources")?.Elements("Item");
if (xElements != null)
foreach (var source in xElements)
{
Console.Write(source);
}
xElements is always null. I tried using namespace as well, it did not work. How can I resolve this issue?
Try below code:
string stringXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><string xmlns=\"http://tempuri.org/\"><Sources><Item><Id>1</Id><Name>John</Name></Item><Item><Id>2</Id><Name>Max</Name></Item><Item><Id>3</Id><Name>Ricky</Name></Item></Sources></string>";
XDocument xDoc = XDocument.Parse(stringXml);
var items = xDoc.Descendants("{http://tempuri.org/}Sources")?.Descendants("{http://tempuri.org/}Item").ToList();
I tested it and it correctly shows that items has 3 lements :) Maybe you used namespaces differently (it's enough to inspect xDoc objct in object browser and see its namespace).
You need to concatenate the namespace and can directly use Descendants method to fetch all Item nodes like:
XNamespace ns ="http://tempuri.org/";
var xDoc = XDocument.Parse(xmlString);
var xElements = xDoc.Descendants(ns + "Item");
foreach (var source in xElements)
{
Console.Write(source);
}
This prints on Console:
<Item xmlns="http://tempuri.org/">
<Id>1</Id>
<Name>John</Name>
</Item><Item xmlns="http://tempuri.org/">
<Id>2</Id>
<Name>Max</Name>
</Item><Item xmlns="http://tempuri.org/">
<Id>3</Id>
<Name>Ricky</Name>
</Item>
See the working DEMO Fiddle

How to get enclosure url with XElement C# Console

I read multiple feed from many sources with C# Console, and i have this code where i load XML From sources:
XmlDocument doc = new XmlDocument();
doc.Load(sourceURLX);
XElement xdoc = XElement.Load(sourceURLX);
How to get enclosure url and show as variable?
If I understand your question correctly (I'm making a big assumption here) - you want to select an attribute from the root (or 'enclosing') tag, named 'url'?
You can make use of XPath queries here. Consider the following XML:
<?xml version="1.0" encoding="utf-8"?>
<root url='google.com'>
<inner />
</root>
You could use the following code to retrieve 'google.com':
String query = "/root[1]/#url";
XmlDocument doc = new XmlDocument();
doc.Load(sourceURLX);
String value = doc.SelectSingleNode(query).InnerText;
Further information about XPath syntax can be found here.
Edit: As you stated in your comment, you are working with the following XML:
<item>
<description>
</description>
<enclosure url="blablabla.com/img.jpg" />
</item>
Therefore, you can retrieve the url using the following XPath query:
/item[1]/enclosure[1]/#url
With xml like below
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>title</title>
<link>https://www.link.com</link>
<description>description</description>
<item>
<title>RSS</title>
<link>https://www.link.com/xml/xml_rss.asp</link>
<description>description</description>
<enclosure url="https://www.link.com/media/test.wmv"
length="10000"
type="video/wmv"/>
</item>
</channel>
</rss>
You will get url by reading attribute
var document = XDocument.Load(sourceURLX);
var url = document.Root
.Element("channel")
.Element("item")
.Element("enclosure")
.Attribute("url")
.Value;
To get multiple urls
var urls = document.Descendants("item")
.Select(item => item.Element("enclosure").Attribute("url").Value)
.ToList();
Using foreach loop
foreach (var item in document.Descendants("item"))
{
var title = item.Element("title").Value;
var link = item.Element("link").Value;
var description = item.Element("description").Value;
var url = item.Element("enclosure").Attribute("url").Value;
// save values to database
}

Unable to use InsertAfter for XmlDocument in c#

Hi below are the XML files which is master XML
<?xml version="1.0" encoding="utf-16"?>
<Verify>
<ver>
<ECU>
<values>
</values>
</ECU>
</ver>
</Verify>
I have multiple files which are of same structure as below
<?xml version="1.0" encoding="utf-16"?>
<Verify>
<ver>
<ECU>
<values>
</values>
</ECU>
</ver>
</Verify>
I want my output as
<?xml version="1.0" encoding="utf-16"?>
<Verify>
<ver>
<ECU>
<values>
</values>
</ECU>
<ECU>
<values>
</values>
</ECU>
<ECU>
<values>
</values>
</ECU>
</ver>
</Verify>
I am using below code to read first one as master xml
and other files from xmls folder. I want to add ECU node from these files under ECU node of master file.
XmlDocument xmlMaster = new XmlDocument();
xmlMaster.Load(#"C:\MasterXMLFile.xml");
XmlElement masterRoot = xmlMaster.DocumentElement;
XmlNode masterParent = masterRoot.LastChild.LastChild;
var downloadfolder = #"C:\AllXMLs\xmls\";
string[] files = Directory.GetFiles(downloadfolder);
foreach (var xx in files)
{
XmlNode masterNode = masterRoot.LastChild.LastChild;
XmlDocument xdoc = new XmlDocument();
xdoc.Load(xx);
XmlElement root = xdoc.DocumentElement;
XmlElement tempNode = (XmlElement)root.LastChild.LastChild;
masterRoot.InsertAfter(tempNode, masterRoot.SelectSingleNode("//ECU").ParentNode);
}
xmlMaster.Save(#"C:\mergeg.xml");
I am getting error at InsertAfter statement as Object reference not set to an instance of an object.
Please suggest me any solution.
Your tempNode is from xdoc document context. You should import it to xmlMaster document context:
XmlNode importedECU = xmlMaster.ImportNode(tempNode, true);
Also instead of InsertAfter it's better to use AppendChild and append new ECU nodes as children of master ver element:
var masterVer = masterRoot.SelectSingleNode("//ver");
foreach(var file in files)
{
var xdoc = new XmlDocument();
xdoc.Load(file);
var tempNode = xdoc.DocumentElement.LastChild.LastChild;
var importedECU = xmlMaster.ImportNode(tempNode, true);
masterVer.AppendChild(importedECU);
}
Your InsertAfter should be on the parentNode of the node you want to insert after, so the parent of tempNode.

How to add Namespace and Declaration to the existing XML

how to add Namespace and Declaration to the existing xml.
My XML
<Order>
<child1></child1>
</Order>
Expected
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Order xmlns="http://a.com/a">
<child1></child>
</Order>
Assuming you're only changing the element namespaces and ignoring any attributes, you can parse this using LINQ to XML and then change every element's qualified name:
var doc = XDocument.Parse(xml);
XNamespace ns = "http://a.com/a";
foreach (var element in doc.Descendants())
{
element.Name = ns + element.Name.LocalName;
}

Add XML node where element value equal n

I have a XML file which looks like
<?xml version="1.0" encoding="utf-8"?>
<Orders>
<Order>
<OrderNumber>123</OrderNumber>
<ClientName>Name</ClientName>
<TotalOrderCost>50</TotalOrderCost>
<Products>
<Product>
<Name>Games</Name>
<Quantity>3</Quantity>
<Price>10</Price>
<TotalProductPrice>30</TotalProductPrice>
</Product>
<Product>
<Name>CDs</Name>
<Quantity>2</Quantity>
<Price>10</Price>
<TotalProductPrice>10</TotalProductPrice>
</Product>
</Products>
</Order>
<Order>
<OrderNumber>456</OrderNumber>
<ClientName>Name 2</ClientName>
<TotalOrderPrice>15</TotalOrderPrice>
<Products>
<Product>
<Name>Tea</Name>
<Quantity>1</Quantity>
<Price>15</Price>
<TotalProductPrice>15</TotalProductPrice>
</Product>
</Products>
</Order>
</Orders>
I have a form where the user selects an order and can add more products in it. I can output the correct XElement which looks like:
<Product>
<Name>Coffee</Name>
<Quantity>1</Quantity>
<Price>15</Price>
<TotalProductPrice>15</TotalProductPrice>
</Product>
How do I add this XElement under the products of an order user has selected? So if the user selects OrderNumer = 456 and adds the product using the form, the XElement goes under 456 order.
I have tried:
xmldoc2.Element("Orders").Element("Order").Element("Products").Add(addProduct);
xmldoc2.Save(orderFilePath);
which always adds the product to the first order on the XML and I can see the reason for it however I cannot see how I could add under the product under the order selected.
You should select order element to which you want to add new product:
int number = 456;
var order = xmldoc2.Element("Orders").Element("Order")
.FirstOrDefault(o => (int)o.Element("OrderNumber") == number);
// check if order not null
order.Element("Products").Add(addProduct);
You also can use XPath for selecting order:
var xpath = String.Format("//Order[OrderNumber[text()={0}]]", number);
var order = xmldoc2.XPathSelectElement(xpath);

Categories

Resources