how to get value of child nodes in xml one by one - c#

How can i read asin and its value from This xml file.
i have multiple product in this xml file.
This two products asin i want to get.
XDocument xdoc = XDocument.Load(#"D:\Product\WriteText2.xml");
XElement match = xdoc.Root.Element("GetMatchingProductForIdResult");
foreach(XElement product in match.Elements("Products"))
{
XElement asin = product.Element("Identifiers").Element("MarketplaceASIN").Element("ASIN");
string asinValue = asin.Value;
}
<GetMatchingProductForIdResult Id="619659000431" IdType="UPC" status="Success">
<Products>
<Product>
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>A21TJRUUN4KGV</MarketplaceId>
<ASIN>B002U1ZBG0</ASIN>
</MarketplaceASIN>
</Identifiers>
</Product>
</Products>
</GetMatchingProductForIdResult>
<GetMatchingProductForIdResult Id="190198462411" IdType="UPC" status="Success">
<Products>
<Product>
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>A21TJRUUN4KGV</MarketplaceId>
<ASIN>B073Q5R6VR</ASIN>
</MarketplaceASIN>
</Identifiers>
</Product>
</Products>
</GetMatchingProductForIdResult>

You can use System.Xml.Linq:
XDocument xdoc = XDocument.Load("file.xml");
XElement match = xdoc.Element("GetMatchingProductForIdResult");
foreach(XElement product in match.Elements("Products")){
XElement asin = product.Element("Identifiers")
.Element("MarketplaceASIN").XElement("ASIN");
string asinValue = asin.Value;
}

Related

How to merge multiple XML files with data in same format in it to a single XML file with all data together using C#.NET?

I have a blob (XYZ) that receives multiple XML files in same format. See below
XML file 1:
<Product>
<ID>001</ID>
<Name>John</Name>
<Designation>Developer</Designation>
</Product>
XML file 2:
<Product>
<ID>002</ID>
<Name>Peter</Name>
<Designation>Tester</Designation>
</Product>
XML file 3:
<Product>
<ID>003</ID>
<Name>Arun</Name>
<Designation>Support</Designation>
</Product>
XML file 4:
<Product>
<ID>004</ID>
<Name>Swetha</Name>
<Designation>Analyst</Designation>
</Product>
XML file 5:
<Product>
<ID>005</ID>
<Name>Gokul</Name>
<Designation>Maintainence</Designation>
</Product>
I need to merge all these files to a single XML file like below and put into another blob (ABC).
Merged XML file:
<xml>
<Product>
<ID>001</ID>
<Name>John</Name>
<Designation>Developer</Designation>
</Product>
<Product>
<ID>002</ID>
<Name>Peter</Name>
<Designation>Tester</Designation>
</Product>
<Product>
<ID>003</ID>
<Name>Arun</Name>
<Designation>Support</Designation>
</Product>
<Product>
<ID>004</ID>
<Name>Swetha</Name>
<Designation>Analyst</Designation>
</Product>
<Product>
<ID>005</ID>
<Name>Gokul</Name>
<Designation>Maintainence</Designation>
</Product>
</xml>
I may need one like this.
What I have tried so far is below one.
using (var jw = new XmlTextWriter(sw)) //sw holds the o/p location to store the merged files
{
jw.WriteStartElement("root");
int i = 0;
int c = list.Count();
foreach (var item in list)
{
if (i > 0)
await jw.WriteRawAsync("\r\n");
var blobdata = await OutputContainerService.GetContentAsync(input.InputLocation + "/" + item);
await jw.WriteRawAsync(blobdata);
i++;
}
jw.WriteEndElement();
await jw.FlushAsync();
}
How could I achieve this using C#.NET?
Option 1 :
Create Product Class
Populate the Product object with values by de-serializing the xml
Add the product object to List
Serialize the List
Option 2:
read the xml to XML Document
populate the product xml as xml node
Add the xml nodes to xml documents

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")

display xml using linq

MessageBox.Show("Hi");
string xml = #"<Table>
<Product>
<Product_id>1</Product_id>
<Product_name>Product 1</Product_name>
<Product_price>1000</Product_price>
</Product>
<Product><Product_id>2</Product_id><Product_name>Product 2</Product_name><Product_price>2000</Product_price></Product>
<Product><Product_id>3</Product_id><Product_name>Product 3</Product_name><Product_price>3000</Product_price></Product>
<Product><Product_id>4</Product_id><Product_name>Product 4</Product_name><Product_price>4000</Product_price></Product>
</Table>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("Product_id"))
{
Console.WriteLine(element);
}
Upon button click only Hi MessageBox is displayed. The elements are not displayed.
Please try this. The values will be shown in the console.
MessageBox.Show("Hi");
string xml = #"
<Table>
<Product>
<Product_id>1</Product_id>
<Product_name>Product 1</Product_name>
<Product_price>1000</Product_price>
</Product>
<Product><Product_id>2</Product_id><Product_name>Product 2</Product_name><Product_price>2000</Product_price></Product>
<Product><Product_id>3</Product_id><Product_name>Product 3</Product_name><Product_price>3000</Product_price></Product>
<Product><Product_id>4</Product_id><Product_name>Product 4</Product_name><Product_price>4000</Product_price></Product>
</Table>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("Product_id"))
{
Console.WriteLine(element.Value);
}
Console.Read();
You should call Console.WriteLine(element.Value);

How can be this xml parsed in efficient way?

I am beginner in C#.
Simple Example of bigger case:
Input:
<?xml version="1.0" encoding="utf-8"?>
<products>
<product>
<id>1</id>
<name>John</name>
</product>
<product>
<id>2</id>
<name>Tom</name>
</product>
<product>
<id>3</id>
<name>Sam</name>
</product>
</products>
</xml>
Output(for id=1):
<id>2</id>
<name>Tom</name>
My part code try psedocode:
XDocument doc=XDocument.Parse(".............");
var els= doc.Descendants("product");
foreach(e in els){
node=e.Element("id");
if(2==node.Value){
return e;
}
}
Please help,
Thanks
Currently your xml file is not well-formatted - remove closing </xml> tag from your file to make it valid. And here is the query:
int id = 1;
XDocument xdoc = XDocument.Load(path_to_xml);
var product = xdoc.Descendants("product")
.Where(p => (int)p.Element("id") == id)
.SingleOrDefault();
This query will return whole <product> element or null if match not found.
Also I believe product name will be enough for you to select (because you already have product id):
var name = xdoc.Descendants("product")
.Where(p => (int)p.Element("id") == id)
.Select(p => (string)p.Element("name"))
.SingleOrDefault();
Returns Tom for id = 2
This will return the product (as in your question) not the id
var product = doc.XPathSelectElement("//product[id and id[text() = '1']]");
You might be looking for XPath:
root.XPathSelectElements(#"//products/product/id[text()='2']")
Edit To the comment: Directly getting the name: //products/product/id[text()='2']/../name
See full example
using System.Xml.Linq;
using System.Xml.XPath;
public class Program
{
public static void Main(string[] args)
{
var doc = XDocument.Parse(XML);
foreach(var n in doc.Root.XPathSelectElements(
#"//products/product/id[text()='2']"))
{
System.Console.WriteLine("Not that hard: '{0}'", n.Parent.Element("name").Value);
}
// Direct query for name:
foreach(var n in doc.Root.XPathSelectElements(
#"//products/product/id[text()='2']/../name"))
{
System.Console.WriteLine("Directly: '{0}'", n.Value);
}
}
private const string XML =
#"<?xml version=""1.0"" encoding=""utf-8""?>
<products>
<product>
<id>1</id>
<name>John</name>
</product>
<product>
<id>2</id>
<name>Tom</name>
</product>
<product>
<id>3</id>
<name>Sam</name>
</product>
</products>";
}
Printing:
Not that hard: 'Tom'
Directly: 'Tom'

LINQ to XML query from querystring

I need to create xml files based on querystring values.
My xml file:
<Products>
<Product>
<Name>Name</Name>
<Category>Books</Category>
<SubCategory>Drama</SubCategory>
</Product>
<Product>
<Name>Name</Name>
<Category>Books</Category>
<SubCategory>Action</SubCategory>
</Product>
<Product>
<Name>Name</Name>
<Category>Paper</Category>
<SubCategory></SubCategory>
</Product>
So if i type ?filter=Books,Paper i need to select Product where Categorycontains value from querystring.
Then if i type ?filter=Books,Paper&filter2=Drama i still need Product where Category contains filter1 but if Product element containsSubCategory that containsfilter2 i need to select just those.
So with: ?filter=Books,Paper&filter2=Drama i need to get xml that looks like this:
<Products>
<Product>
<Name>Name</Name>
<Category>Books</Category>
<SubCategory>Drama</SubCategory>
</Product>
<Product>
<Name>Name</Name>
<Category>Paper</Category>
<SubCategory></SubCategory>
</Product>
</Products>
Also some products may have empty SubCategory element. I don't know if that is important.
My query looks like this:
var items = from el in SimpleStreamAxis(esysPath, "Product")
where filter.Contains(el.Element("Category").Value.Trim())
where filter1.Contains(el.Element("SubCategory").Value.Trim())
select new
{
ProductID = el.Element("ID").Value,
Name = el.Element("Name").Value,
Price = el.Element("Price").Value,
Picture = el.Element("Picture").Value
};
This is selecting all Product elements where filter1 contains SubCategory.
So can anyone point me in to right direction on how to write this query.
Thanks.
This should get you started in the right direction, it will find all products where the Category element is either Book or Paper
List<string> categories = new List<string() {"Book", "Paper"};
XDocument doc = XDocument.Parse("Your xml string");
var products = doc.Descendants("Product")
.Where(el => categories.Contains(el.Element("Category").Value));

Categories