How to get value of same nodes in XML in c# - c#

I want to get the value of artist (Bob Dylan) for title "Greatest Hits" in the xml below
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<price>10.90</price>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<price>10.0</price>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Bob Dylan</artist>
<price>10.90</price>
</cd>
</catalog>

Adapt it as you need ...
using System.Xml;
static void GetArtistFromXml()
{
var xml = "<?xml version=\"1.0\" encoding=\"ISO - 8859 - 1\"?><catalog><cd><title>Empire Burlesque</title><artist>Bob Dylan</artist><price>10.90</price></cd><cd><title>Hide your heart</title><artist>Bonnie Tyler</artist><price>10.0</price></cd><cd><title>Greatest Hits</title><artist>Bob Dylan</artist><price>10.90</price></cd></catalog>";
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
var artistElement = xmlDocument.DocumentElement.SelectSingleNode("//cd[title[text()=\"Greatest Hits\"]]/artist");
Console.WriteLine(artistElement.InnerText);
}

It is better to use LINQ to XML API. It is available in the .Net Framework since 2007.
c#
void Main()
{
const string filename = #"e:\Temp\AmeyP.xml";
XDocument xdoc = XDocument.Load(filename);
string artist = xdoc.Descendants("cd")
.Where(x => x.Element("title").Value.Equals("Greatest Hits"))
.Elements("artist").FirstOrDefault()?.Value;
Console.WriteLine(artist);
}
Output
Bob Dylan

Related

How to merge one specific tag into one XML using c#?

I have two XMLs'
XML1:
'<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<Object>
<Value>3421</Value>
<Value>John</Value>
</Object>
</Sequence>
</Data>'
XML2:
'<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<Object>
<Value>1234</Value>
<Value>SAM</Value>
</Object>
</Sequence>
</Data>'
I want the output like below
'<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<Object>
<Value>3421</Value>
<Value>John</Value>
</Object>
<Object>
<Value>1234</Value>
<Value>SAM</Value>
</Object>
</Sequence>
</Data>'
I.e I want to merge Object tag from XML2 to XML1 using C# code.
Could someone please help me?
You can use XPath to select the nodes you need and then simply use .NET xml using System.Xml
For more information look at https://www.w3schools.com/xml/xpath_intro.asp
Load Xml Documents
I saved the two sample xml-files you provided to separate files and imported them like this
XmlDocument doc1 = new XmlDocument();
XmlDocument doc2 = new XmlDocument();
using (var sw = new StreamReader("xml1.xml"))
{
var text = sw.ReadToEnd();
doc1.LoadXml(text);
}
using (var sw = new StreamReader("xml2.xml"))
{
var text = sw.ReadToEnd();
doc2.LoadXml(text);
}
Select nodes with XPATH
We will take all elements that have the name 'object' and add them to child of the other xml's 'sequence'-element. Therefore we select the 'sequence'-element of one document and the 'object'elements of the other document.
var sequenceNodes = doc1.SelectSingleNode("/Data/Sequence");
var objectNodes = doc2.SelectNodes("/Data/Sequence/Object");
Concat the nodes into one document
Then we take each 'object'-element, import it into the other document-context and append it under the 'sequence'-node
foreach (XmlNode node in objectNodes)
{
XmlNode importedNode = doc1.ImportNode(node, true);
sequenceNodes.AppendChild(importedNode);
}
Output the file
using (var stringWriter = new StringWriter())
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
doc1.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();
File.AppendAllText("out.xml", stringWriter.GetStringBuilder().ToString());
}
The outputfile looks like this:
Please, take a look at this solution:
what-is-the-fastest-way-to-combine-two-xml-files-into-one
Using Union method seems to be what you are seeking.
Run an XSLT transformation:
<xsl:template name="merge">
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<xsl:copy-of select="document('a.xml')//Object"/>
<xsl:copy-of select="document('b.xml')//Object"/>
</Sequence>
</Data>
</xsl:template>
Try following linq which joins all filenames with same value.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication166
{
class Program
{
const string FILENAME1 = #"c:\temp\test.xml";
const string FILENAME2 = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XDocument doc1 = XDocument.Load(FILENAME1);
XNamespace ns1 = doc1.Root.GetDefaultNamespace();
XDocument doc2 = XDocument.Load(FILENAME2);
XNamespace ns2 = doc2.Root.GetDefaultNamespace();
var joins= from d1 in doc1.Descendants(ns1 + "Data")
join d2 in doc2.Descendants(ns2 + "Data")
on (string)d1.Element(ns1 + "Filename") equals (string)d2.Element(ns2 + "Filename")
select new { d1 = d1, d2 = d2};
foreach (var join in joins)
{
XElement d2Object = join.d2.Descendants("Object").FirstOrDefault();
join.d1.Descendants("Sequence").FirstOrDefault().Add(XElement.Parse(d2Object.ToString()));
}
}
}
}

c# Merging two XMLs giving error

I am trying to merge two XMLs with same structure but different data into one.
I am getting this error: A node of type Document cannot be added to content.
Below is my code
var productElements =
testGroupProvider.GetTestGroup().ProductTests.Select(
productTest => new XElement(xNamespace + "Product",
new XElement(xNamespace + "ExternalId", productTest.ProductNameKey),
new XElement(xNamespace + "Name", testGroupProvider.GetProductName(productTest)),
new XElement(xNamespace + "ImageUrl", ChoiceBaseHostName + GetProductImageUrl(productTest, TargetDatabase))));
var root = new XDocument(
new XElement(xNamespace + "Feed",
new XAttribute("xmlns", xNamespace),
new XAttribute("name", BVFeedsName),
new XAttribute("incremental", "true"),
new XAttribute("extractDate", DateTime.Now.ToString("o")),
new XElement(xNamespace + "Categories",
new XElement(xNamespace + "Category",
new XElement(xNamespace + "ExternalId", testGroupProvider.GetProductGroup().Id),
new XElement(xNamespace + "Name", testGroupProvider.GetProductGroup().Name),
testGroupProvider.GetTestGroup().Name),
new XElement(xNamespace + "Products", productElements)));
var filePath = #"D:\testXML\test.xml";
XElement xml = XElement.Load(filePath);
xml.Add(root);
xml.Save(filePath);
Can anyone tell me what i am doing wrong.
This is the XML structure in test.xml
<?xml version="1.0" encoding="utf-8"?>
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
<Categories>
<Category>
<ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
<Name>Cereal and muesli</Name>
</Category>
</Categories>
<Products>
<Product>
<ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
<Name>Coles Almond, Hazelnut & Macadamia Cluster Fusions</Name>
<ImageUrl></ImageUrl>
</Product>
</Products>
</Feed>
The second XML has the same structure with different products
<?xml version="1.0" encoding="utf-8"?>
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
<Categories>
<Category>
<ExternalId>{12}</ExternalId>
<Name>cat1</Name>
</Category>
</Categories>
<Products>
<Product>
<ExternalId>Id</ExternalId>
<Name>Ccoles</Name>
<ImageUrl></ImageUrl>
</Product>
</Products>
</Feed>
I want to combine them like below
<?xml version="1.0" encoding="utf-8"?>
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
<Categories>
<Category>
<ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
<Name>Cereal and muesli</Name>
</Category>
<Category>
<ExternalId>{12}</ExternalId>
<Name>cat1</Name>
</Category>
</Categories>
<Products>
<Product>
<ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
<Name>Coles Almond, Hazelnut & Macadamia Cluster Fusions</Name>
<ImageUrl></ImageUrl>
</Product>
<Product>
<ExternalId>Id</ExternalId>
<Name>Ccoles</Name>
<ImageUrl></ImageUrl>
</Product>
</Products>
</Feed>
A xml document must have only one root.
Working with the documents you attached, you can replace the xml.Add(root); with the following (i.e. it will add each node under one root to the other xml root)
foreach (var child in root.Root.Elements())
{
xml.Element(child.Name.ToString()).Add(child.Nodes());
}
Edit - A further generalization
You can generalize the above code using a Merge extension of 2 XElements so that it reads as follows
foreach (var child in root.Elements())
{
xml.Element(child.Name.ToString()).Merge(child, xNamespace + "ExternalId");
}
Having defined the extension
public static void Merge(this XElement root1, XElement root2, XName element_id)
{
root1.Add(root2.Elements().Except(root1.Elements(), new MyComparer(element_id)));
}
with a xml comparer
public class MyComparer : IEqualityComparer<XElement>
{
private XName _element_id;
public MyComparer(XName element_id)
{
_element_id = element_id;
}
public bool Equals(XElement x, XElement y)
{
return x.Element(_element_id).Value.Equals(y.Element(_element_id).Value);
}
public int GetHashCode(XElement el)
{
return el.Element(_element_id).Value.GetHashCode();
}
}
Select correct nodes to add and correct nodes to be added.
var filePath = #"D:\testXML\test.xml";
XElement xml = XElement.Load(filePath);
var xmlCategories = xml.Descendants("Categories").First();
var rootCategories = root.Descendants("Category");
xmlCategories.Add(rootCategories);
var xmlProducts = xml.Descendants("Products").First();
var rootProducts = root.Descendants("Product");
xmlProducts.Add(rootProducts);
xml.Save(filePath);
Be crystal clear what you are doing.
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME1 = #"c:\temp\test1.xml";
const string FILENAME2 = #"c:\temp\test2.xml";
static void Main(string[] args)
{
XDocument doc1 = XDocument.Load(FILENAME1);
XDocument doc2 = XDocument.Load(FILENAME2);
XElement category1 = doc1.Descendants().Where(x => x.Name.LocalName == "Categories").FirstOrDefault();
XElement category2 = doc2.Descendants().Where(x => x.Name.LocalName == "Categories").FirstOrDefault();
category1.Add(category2.Descendants());
XElement product1 = doc1.Descendants().Where(x => x.Name.LocalName == "Products").FirstOrDefault();
XElement product2 = doc2.Descendants().Where(x => x.Name.LocalName == "Products").FirstOrDefault();
product1.Add(product2.Descendants());
}
}
}
Try this, sorry about the VB
'second is The second XML has the same structure with different products
Dim combined As XElement = New XElement(test) 'create copy of test.xml
combined.<Categories>.LastOrDefault.Add(second.<Categories>.Elements)
combined.<Products>.LastOrDefault.Add(second.<Products>.Elements)
or
'if test can be used to combine then
test.<Categories>.LastOrDefault.Add(second.<Categories>.Elements)
test.<Products>.LastOrDefault.Add(second.<Products>.Elements)
The result is
<Feed name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00" xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6">
<Categories>
<Category>
<ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
<Name>Cereal and muesli</Name>
</Category>
<Category>
<ExternalId>{12}</ExternalId>
<Name>cat1</Name>
</Category>
</Categories>
<Products>
<Product>
<ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
<Name>Coles Almond, Hazelnut & Macadamia Cluster Fusions</Name>
<ImageUrl></ImageUrl>
</Product>
<Product>
<ExternalId>Id</ExternalId>
<Name>Ccoles</Name>
<ImageUrl></ImageUrl>
</Product>
</Products>
</Feed>
The test data I used is
Dim test As XElement =
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
<Categories>
<Category>
<ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
<Name>Cereal and muesli</Name>
</Category>
</Categories>
<Products>
<Product>
<ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
<Name>Coles Almond, Hazelnut & Macadamia Cluster Fusions</Name>
<ImageUrl></ImageUrl>
</Product>
</Products>
</Feed>
Dim second As XElement =
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
<Categories>
<Category>
<ExternalId>{12}</ExternalId>
<Name>cat1</Name>
</Category>
</Categories>
<Products>
<Product>
<ExternalId>Id</ExternalId>
<Name>Ccoles</Name>
<ImageUrl></ImageUrl>
</Product>
</Products>
</Feed>
The XElements can be loaded like this
test = XElement.Load("PATH")
second = XElement.Load("second PATH")
and saved like this
test.Save("PATH")
second.Save("second PATH")
combined.Save("combined PATH")

c#: Add element to second nesting in xml

I have a XML which looks like:
<users>
<user id="0">
<name>John</name>
<lastName>Smith</lastName>
<bills>
<bill id="0">
<name>Water</name>
<forMonth>2013-12-01</forMonth>
<money>235</money>
<lastDayToPay>2014-01-02</lastDayToPay>
<payed>False</payed>
</bill>
<bill id="1">
<name>Telephone</name>
<forMonth>2013-11-01</forMonth>
<money>690</money>
<lastDayToPay>2014-01-01</lastDayToPay>
<payed>True</payed>
</bill>
</bills>
</user>
How can i add new bill for the user, i have problem accessing "bills" node and adding element to it. I'm using c#.
use following code
XmlDocument myDocument = new XmlDocument();
myDocument.Load(XMLFile);
XmlNode newNode = myDocument.CreateElement("bill");
//add values;
var requiredNode = myDocument.ChildNodes.OfType<XmlElement>().Where(o => o.Name == "bills").First();
requiredNode.AppendChild(newNode);
myDocument.Save(XMLFile);

Finding and Filtering XML Data

I have an XML document here:
<?xml version="1.0" encoding="utf-8" ?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
<CD>
<TITLE>Test Title 1</TITLE>
<ARTIST>Test Name 1</ARTIST>
<COUNTRY>Test Country 1</COUNTRY>
<COMPANY>Test Company 1</COMPANY>
<PRICE>100.00</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Test Title 3</TITLE>
<ARTIST>Test Name 3</ARTIST>
<COUNTRY>Test Country 3</COUNTRY>
<COMPANY>Test Company 3</COMPANY>
<PRICE>1.99</PRICE>
<YEAR>1984</YEAR>
</CD>
<CD>
<TITLE>Test Title 2</TITLE>
<ARTIST>Test Name 2</ARTIST>
<COUNTRY>Test Country 2</COUNTRY>
<COMPANY>Test Company 2</COMPANY>
<PRICE>19.99</PRICE>
<YEAR>1985</YEAR>
</CD>
</CATALOG>
What I am trying to do is find all the CDs that have a year of 1985. I'm very new to LINQ to XML and I have no idea what I'm doing. Because most internet resources are very specific example, I'm having trouble applying it to my example.
Here's the C# that I've coded so far:
namespace ReadingXML
{
class Program
{
static void Main(string[] args)
{
XElement xelement = XElement.Load("..\\..\\music.xml");
IEnumerable<XElement> music = xelement.Elements();
/*// Read the entire XML
foreach (var item in catalogues)
{
Console.WriteLine(item);
}
Console.ReadLine();*/
var query = from item in music.Descendants("CD")
select new { year = item.Element("YEAR").Equals(1985) };
foreach (var item in query)
Console.WriteLine(item.ToString());
Console.ReadLine();
}
}
}
Can someone please tell me how I can achieve what I'm trying to do and/or why my code isn't functioning?
XDocument X = XDocument.Load(#"XMLFileLocation");
var CDsIn1985 = X.Element("CATALOG").Elements("CD").Where(E => E.Element("YEAR").Value == "1985");
foreach (var item in CDsIn1985)
{
Console.WriteLine(String.Format("Title : {0}, Artist : {1}", item.Element("TITLE").Value, item.Element("ARTIST").Value));
}
Another away is to use String Interpolation
foreach (var item in CDsIn1985)
{
Console.WriteLine(String.Format($"Title : {item.Element("TITLE").Value}, Artist : { item.Element("ARTIST").Value}"));
}

how can append to xml

i have this xml.
<project>
<user>
<id>1</id>
<name>a</name>
</user>
<user>
<id>2</id>
<name>b</name>
</user>
</project>
now how can append a new element like this between element <project></project>
<user>
<id>3</id>
<name>c</name>
</user>
string xml =
#"<project>
<user>
<id>1</id>
<name>a</name>
</user>
<user>
<id>2</id>
<name>b</name>
</user>
</project>";
XElement x = XElement.Load(new StringReader(xml));
x.Add(new XElement("user", new XElement("id",3),new XElement("name","c") ));
string newXml = x.ToString();
If you mean using C# then probably the simplest way is to load the xml up into an XmlDocument object and then add a node representing the additional element.
e.g. something like:
string filePath = "original.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filePath);
XmlElement root = xmlDoc.DocumentElement;
XmlNode nodeToAdd = doc.CreateElement(XmlNodeType.Element, "user", null);
XmlNode idNode = doc.CreateElement(XmlNodeType.Element, "id", null);
idNode.InnerText = "1";
XmlNode nameNode = doc.CreateElement(XmlNodeType.Element, "name", null);
nameNode.InnerText = "a";
nodeToAdd.AppendChild(idNode);
nodeToAdd.AppendChild(nameNode);
root.AppendChild(nodeToAdd);
xmlDoc.Save(filePath); // Overwrite or replace with new file name
But you haven't said where the xml fragments are - in files/strings?
If you have the below XML file:
<CATALOG>
<CD>
<TITLE> ... </TITLE>
<ARTIST> ... </ARTIST>
<YEAR> ... </YEAR>
</CD>
</CATALOG>
and you have to add another <CD> node with all its child nodes:
using System.Xml; //use the xml library in C#
XmlDocument document = new XmlDocument(); //creating XML document
document.Load(#"pathOfXmlFile"); //load the xml file contents into the newly created document
XmlNode root = document.DocumentElement; //points to the root element (catalog)
XmlElement cd = document.CreateElement("CD"); // create a new node (CD)
XmlElement title = document.CreateElement("TITLE");
title.InnerXML = " ... "; //fill-in the title value
cd.AppendChild(title); // append title to cd
XmlElement artist = document.CreateElement("ARTIST");
artist.InnerXML = " ... ";
cd.AppendChild(artist);
XmlElement year = document.CreateElement("YEAR");
year.InnerXML = " ... ";
cd.AppendChild(year);
root.AppendChild(cd); // append cd to the root (catalog)
document.save(#"savePath");//save the document

Categories