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
I wanted to select a specific data based on its id and here are the example of the xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<productdetails>
<product>
<Id>1</Id>
<product_name>banana</product_name>
<product_price>5.00</product_price>
<product_description>its banana</product_description>
<product_quantity>12</product_quantity>
</product>
<product>
<Id>2</Id>
<product_name>mango</product_name>
<product_price>10.00</product_price>
<product_description>its mango</product_description>
<product_quantity>12</product_quantity>
</product>
<product>
<Id>3</Id>
<product_name>orange</product_name>
<product_price>3.00</product_price>
<product_description>its orange</product_description>
<product_quantity>1</product_quantity>
</product>
<product>
<Id>4</Id>
<product_name>apple</product_name>
<product_price>4.00</product_price>
<product_description>its apple</product_description>
<product_quantity>1</product_quantity>
</product>
</productdetails>
What im trying to do is store them back in a different but temporary xml file to then be calculated in GridView.
Hope I understood your query correctly. You could search for specific data based on its Id using Linq. For example,
var root = XElement.Parse(xmlString);
var idToSearch = 1;
var result= root.Elements("product")
.Where(x=>Convert.ToInt32(x.Element("Id").Value)==idToSearch)
.Select(x=>new
{
ProductName=x.Element("product_name").Value,
Price=x.Element("product_price").Value
});
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;
}
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")
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'