How can be this xml parsed in efficient way? - c#

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'

Related

convert dataset into nested xml???Help me

I have 1 dataset, named Invoices, of 2 datatable, the data is taken from sql server 2008. Ignoring the data from the database, I focus on the dataset exported to xml. Datatable 1, named Invoice, includes OrderID, CustomerID, CustomerName, CustomerPhone.
Datatable 2, named Products, includes ProductID, OrderID, ProductName , Price, Quantity, Amount.
My Question is, I want to get this output,
<Invoices>
<OrderID>1</OrderID>
<Invoice>
<CustomerID>1</CustomerID>
<CustomerName>A</CustomerName>
<CustomerPhone>123</CustomerPhone>
<Products>
<Product>
<ProductID>1</ProductID>
<ProductName>C</ProductName>
<Price>10</Price>
<Quantity>2</Quantity>
<Amount>20</Amount>
</Product>
</Products>
<TotalAmount>20</TotalAmount> --TotalAmount = sum(amount of products)
</Invoice>
<OrderID>2</OrderID>
<Invoice>
<CustomerID>3</CustomerID>
<CustomerName>D</CustomerName>
<CustomerPhone>1789</CustomerPhone>
<Products>
<Product>
<ProductID>5</ProductID>
<ProductName>V</ProductName>
<Price>30</Price>
<Quantity>3</Quantity>
<Amount>90</Amount>
</Product>
<Product>
<ProductID>9</ProductID>
<ProductName>Z</ProductName>
<Price>5</Price>
<Quantity>4</Quantity>
<Amount>20</Amount>
</Product>
</Products>
<TotalAmount>110</TotalAmount> --TotalAmount = sum(amount of products)
</Invoice>
</Invoices>
But I get this output:
<Invoices>
<OrderID>1</OrderID>
<Invoice>
<CustomerID>1</CustomerID>
<CustomerName>A</CustomerName>
<CustomerPhone>123</CustomerPhone>
<TotalAmount>20</TotalAmount> --TotalAmount = sum(amount of products)
<Products>
<Product>
<ProductID>1</ProductID>
<ProductName>C</ProductName>
<Price>10</Price>
<Quantity>2</Quantity>
<Amount>20</Amount>
</Product>
</Products>
</Invoice>
<OrderID>2</OrderID>
<Invoice>
<CustomerID>3</CustomerID>
<CustomerName>D</CustomerName>
<CustomerPhone>1789</CustomerPhone>
<TotalAmount>110</TotalAmount> --TotalAmount = sum(amount of products)
<Products>
<Product>
<ProductID>5</ProductID>
<ProductName>V</ProductName>
<Price>30</Price>
<Quantity>3</Quantity>
<Amount>90</Amount>
</Product>
<Product>
<ProductID>9</ProductID>
<ProductName>Z</ProductName>
<Price>5</Price>
<Quantity>4</Quantity>
<Amount>20</Amount>
</Product>
</Products>
</Invoice>
</Invoices>
The code to export xml is:
public DataSet LoadInvoices()
{
DataSet ds = new DataSet("Invoices");
DataTable dt1 = LoadInvoice();
dt1.TableName = "Invoice";
DataTable dt3 = LoadProduct();
dt3.TableName = "Product";
ds.Tables.Add(dt1);
ds.Tables.Add(dt3);
DataColumn colDT3 = dt3.Columns[2];
DataColumn colDT21 = dt1.Columns[0];
DataRelation rel2 = new DataRelation("PRODUCT"
, colDT21, colDT3);
rel2.Nested = true;
ds.Relations.Add(rel2);
return ds;
}
public string xmlConvert(DataSet ds)
{
string sXML = "";
sXML = ds.GetXml();
return sXML;
}
Therefore, I have to adjust the code to export the xml as I want???
You have invalid xml since it contains and ampersand. Should be & The issue probably can be fixed in the dataset query but do not have enough info on the methods that were used to create the xml. You always can edit the xml code Using xml linq to filter output :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> removeItems = doc.Descendants("catname").Where(x => (string)x != "Hotel").ToList();
removeItems.Remove();
}
}
}

How do to read specific data by the id from xml in ASP.Net

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
});

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

Accessing xml elements using LINQ to XML

I have a xml document like this and I need to access the "employees", "employee" elements so I am trying to use linq's XDocument class to get the employee elements but it always returns empty value.
Sample xml:
<organization>
<metadata>
</metadata>
<main>
<otherInfo>
</otherInfo>
<employeeInfo>
<employees>
<employee>
<id>1</id>
<name>ABC</name>
</employee>
<employee>
<id>2</id>
<name>ASE</name>
</employee>
<employee>
<id>3</id>
<name>XYZ</name>
</employee>
</employees>
</employeeInfo>
</main>
</organization>
C# code:
XDocument xDoc = XDocument.Parse(xmlString);
var allEmployees = from d in xDoc.Descendants("employeeInfo")
from ms in d.Elements("employees")
from m in ms.Elements("employee")
select m;
It kind of depends on what information you need. Your select returns an IEnumerable list.
This code will print out each employee
string xmlString = #"<organization>
<metadata>
</metadata>
<main>
<otherInfo>
</otherInfo>
<employeeInfo>
<employees>
<employee>
<id>1</id>
<name>ABC</name>
</employee>
<employee>
<id>2</id>
<name>ASE</name>
</employee>
<employee>
<id>3</id>
<name>XYZ</name>
</employee>
</employees>
</employeeInfo>
</main>
</organization>";
XDocument xDoc = XDocument.Parse(xmlString);
var allEmployees = from d in xDoc.Descendants("employeeInfo")
from ms in d.Elements("employees")
from m in ms.Elements("employee")
select m;
foreach (var emp in allEmployees) {
Console.WriteLine(emp);
}
Console.Read();
XDocument xDoc = XDocument.Parse(xmlString);
var allEmployees = (from r in xDoc.Descendants("employee")
select new
{
Id = r.Element("id").Value,
Name = r.Element("name").Value
}).ToList();
foreach (var r in allEmployees)
{
Console.WriteLine(r.Id + " " + r.Name);
}
Just use Descendants("Employee");
XDocument xDoc = XDocument.Parse(xmlString);
var allEmployees = xDoc.Descendants("employee").ToList();

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