Select list of node by innertext. Issue XmlDocument - c#

I have following Xml
<Main>
<Order Id="1262">
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod2</Name>
<Barcode>2345</Barcode>
</Product>
</Order>
<Order Id="1263">
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod2</Name>
<Barcode>2345</Barcode>
</Product>
</Order>
</Main>
XmlDocument xml=new XmlDocument();
xml.Load(path);
Now I want to select only nodes whose barcode is 1234 from Node whose orderId=1263. My code is
string OrderId="1262"
string ReadedBarcode ="1234"
XmlNode ONode = xml.SelectSingleNode("//Order[#Id='" + OrderId + "']");
XmlNodeList BarCodeNodeList = ONode.SelectNodes("//Product/Barcode[text()='" + ReadedBarcode + "']");
But I dont know why all the nodes from document having innertext 1234 are getting selected. that means even node from <Order Id="1263"> this node is getting selected.
Any Solutions?

This should do it
XmlDocument xml=new XmlDocument();
xml.Load(path);
string OrderId = "1262";
string ReadedBarcode = "1234";
XmlNodeList BarCodeNodeList = xml.SelectNodes("//Order[#Id='" + OrderId + "']"+"//Product/Barcode[text()='" + ReadedBarcode + "']");
Also, your XML is invalid, it is missing some start tags, it should be
<Main>
<Order Id="1262">
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod2</Name>
<Barcode>2345</Barcode>
</Product>
</Order>
<Order Id="1263">
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod2</Name>
<Barcode>2345</Barcode>
</Product>
</Order>
</Main>

I know you wrote that you are 'stuck' with XmlDocument, but the reason you gave sounded like a decision not based on the technical constraints, rather on preference. I believe in a tool-for-the-job, so please forgive me for writing a solution you may not like, but it might persuade you of the merits in using XDocument where you see the benefit.
The XML you posted was not syntactically correct, the sample below shows the corrections where I have added the missing Product starter nodes.
Here's what I would suggest, code written in LinqPad. The Dump() method just spits the variable out to a console. Also, I did not know which 'node' you wanted to find, so I am returning the Order node in this sample.
Cheers, Aaron
var doc = XDocument.Parse(#"
<Main>
<Order Id=""1262"">
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod2</Name>
<Barcode>2345</Barcode>
</Product>
</Order>
<Order Id=""1263"">
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod1</Name>
<Barcode>1234</Barcode>
</Product>
<Product>
<Name>Prod2</Name>
<Barcode>2345</Barcode>
</Product>
</Order>
</Main>
");
var barcode = "1234";
var orderId = "1263";
var found = (
from row in doc.Root.Descendants("Order")
where
row.Attribute("Id") != null &&
row.Attribute("Id").Value == orderId &&
row.Descendants("Barcode").Any(a => a.Value == barcode)
select row).ToList();
found.Dump();

Related

c# XML transformation

i need help for below xml. I wonder how can I bring the XML below, I've been trying for a very long time, but it didn't work. I would be grateful if you could help with this.
I can do the same thing in php, but since my data is a little big, php takes a lot of time, I read the file from the web service with c# and save it as xml, but it didn't work out as I wanted.
<ResultOfProductList xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServerCode>kenan</ServerCode>
<ClientCode>B124565</ClientCode>
<Username>karanfil</Username>
<ClientIPAddress>78.135.235.3</ClientIPAddress>
<Successful>true</Successful>
<RequestDate>2022-07-22T12:28:54.6238843+03:00</RequestDate>
<ElapsedTimeMS>17</ElapsedTimeMS>
<MethodType>GetAllProductsByParts</MethodType>
<Result EndOfProducts="false">
<CustomerCode>B124565</CustomerCode>
<CustomerName>kenan</CustomerName>
<Date>2022-07-22T12:28:54.6238843+03:00</Date>
<Brands>
<Brand ID="174" Lang="tr" BrandName="PARTSMALL-KORE" StandardName=""/>
</Brands>
<Products>
<Product ID="134898" BrandID="174" ProductCode="KR-PML-PTA-086" ProducerCode="" MinOrderAmount="1" PiecesInBox="1" Unit="PCE" New="false">
<ProductNames>
<ProductName Lang="tr">VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
</ProductNames>
<BaseOeNr>43794-22000</BaseOeNr>
<Pricing>
<ListPriceCurrency>USD</ListPriceCurrency>
<LocalCurrency>TLY</LocalCurrency>
<CurrencyRate>17.6599</CurrencyRate>
<ListPriceWoVAT>31.24</ListPriceWoVAT>
<LocalListPriceWVat>651.0004</LocalListPriceWVat>
<LocalListPriceWoVat>551.695251</LocalListPriceWoVat>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<LocalNetPriceWoVat>319.983246</LocalNetPriceWoVat>
<Discount1>42</Discount1>
<Discount2>0</Discount2>
<Discount3>0</Discount3>
<Discount4>0</Discount4>
<Discount5>0</Discount5>
<Discount6>0</Discount6>
<InDiscount>false</InDiscount>
</Pricing>
<Stocks>
<Stock WarehouseID="1" Equality="Eq">0</Stock>
<Stock WarehouseID="7" Equality="Eq">0</Stock>
<Stock WarehouseID="4" Equality="Eq">0</Stock>
<Stock WarehouseID="2" Equality="Eq">0</Stock>
<Stock WarehouseID="5" Equality="Eq">0</Stock>
</Stocks>
</Product>
</Result>
</ResultOfProductList>
Desired output
<Products>
<Product>
<ID>134898</ID>
<BrandID>174</BrandID>
<BaseOeNr>43794-22000</BaseOeNr>
<ProductCode>KR-PML-PTA-086</ProductCode>
<ProductName>VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
<LocalCurrency>TLY</LocalCurrency>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<Stocks>WarehouseID=1 + WarehouseID=7 + WarehouseID=4 + WarehouseID=2 + WarehouseID=5 </Stocks>
</Product>
</Products>
Here is XSLT based solution.
It is just not clear if the <Stocks> element value is what you need.
Input XML
<?xml version="1.0"?>
<ResultOfProductList xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServerCode>kenan</ServerCode>
<ClientCode>B124565</ClientCode>
<Username>karanfil</Username>
<ClientIPAddress>78.135.235.3</ClientIPAddress>
<Successful>true</Successful>
<RequestDate>2022-07-22T12:28:54.6238843+03:00</RequestDate>
<ElapsedTimeMS>17</ElapsedTimeMS>
<MethodType>GetAllProductsByParts</MethodType>
<Result EndOfProducts="false">
<CustomerCode>B124565</CustomerCode>
<CustomerName>kenan</CustomerName>
<Date>2022-07-22T12:28:54.6238843+03:00</Date>
<Brands>
<Brand ID="174" Lang="tr" BrandName="PARTSMALL-KORE" StandardName=""/>
</Brands>
<Products>
<Product ID="134898" BrandID="174" ProductCode="KR-PML-PTA-086" ProducerCode="" MinOrderAmount="1" PiecesInBox="1" Unit="PCE" New="false">
<ProductNames>
<ProductName Lang="tr">VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
</ProductNames>
<BaseOeNr>43794-22000</BaseOeNr>
<Pricing>
<ListPriceCurrency>USD</ListPriceCurrency>
<LocalCurrency>TLY</LocalCurrency>
<CurrencyRate>17.6599</CurrencyRate>
<ListPriceWoVAT>31.24</ListPriceWoVAT>
<LocalListPriceWVat>651.0004</LocalListPriceWVat>
<LocalListPriceWoVat>551.695251</LocalListPriceWoVat>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<LocalNetPriceWoVat>319.983246</LocalNetPriceWoVat>
<Discount1>42</Discount1>
<Discount2>0</Discount2>
<Discount3>0</Discount3>
<Discount4>0</Discount4>
<Discount5>0</Discount5>
<Discount6>0</Discount6>
<InDiscount>false</InDiscount>
</Pricing>
<Stocks>
<Stock WarehouseID="1" Equality="Eq">0</Stock>
<Stock WarehouseID="7" Equality="Eq">0</Stock>
<Stock WarehouseID="4" Equality="Eq">0</Stock>
<Stock WarehouseID="2" Equality="Eq">0</Stock>
<Stock WarehouseID="5" Equality="Eq">0</Stock>
</Stocks>
</Product>
</Products>
</Result>
</ResultOfProductList>
XSLT
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/ResultOfProductList">
<Products>
<xsl:for-each select="Result/Products/Product">
<xsl:variable name="BrandID" select="#BrandID"/>
<Product>
<ID>
<xsl:value-of select="#ID"/>
</ID>
<!--<BrandID>
<xsl:value-of select="#BrandID"/>
</BrandID>-->
<BrandName><xsl:value-of select="../../Brands/Brand[#ID=$BrandID]/#BrandName"/></BrandName>
<BaseOeNr>
<xsl:value-of select="BaseOeNr"/>
</BaseOeNr>
<ProductCode>
<xsl:value-of select="#ProductCode"/>
</ProductCode>
<ProductName>
<xsl:value-of select="ProductNames/ProductName"/>
</ProductName>
<LocalCurrency>
<xsl:value-of select="Pricing/LocalCurrency"/>
</LocalCurrency>
<LocalNetPriceWVat>
<xsl:value-of select="Pricing/LocalNetPriceWVat"/>
</LocalNetPriceWVat>
<Stocks><xsl:value-of select="sum(Stocks/Stock)"/></Stocks>
</Product>
</xsl:for-each>
</Products>
</xsl:template>
</xsl:stylesheet>
Output XML
<Products>
<Product>
<ID>134898</ID>
<BrandName>PARTSMALL-KORE</BrandName>
<BaseOeNr>43794-22000</BaseOeNr>
<ProductCode>KR-PML-PTA-086</ProductCode>
<ProductName>VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
<LocalCurrency>TLY</LocalCurrency>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<Stocks>0</Stocks>
</Product>
</Products>
c#
void Main()
{
const string SOURCEXMLFILE = #"e:\Temp\input.xml";
const string XSLTFILE = #"e:\Temp\process.xslt";
const string OUTPUTXMLFILE = #"e:\temp\output.xml";
try
{
XsltArgumentList xslArg = new XsltArgumentList();
using (XmlReader src = XmlReader.Create(SOURCEXMLFILE))
{
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XSLTFILE, new XsltSettings(true, true), new XmlUrlResolver());
XmlWriterSettings settings = xslt.OutputSettings.Clone();
settings.IndentChars = "\t";
// to remove BOM
settings.Encoding = new UTF8Encoding(false);
using (XmlWriter result = XmlWriter.Create(OUTPUTXMLFILE, settings))
{
xslt.Transform(src, xslArg, result, new XmlUrlResolver());
result.Close();
}
}
Console.WriteLine("File '{0}' has been generated.", OUTPUTXMLFILE);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

How to convert XML Elements to Attributes using c#

I have an XML as below. In this XML all the attributes are available as elements.
<Dress>
<ID>001</ID>
<shirts>
<product>
<ID>345</ID>
<Name>tee</Name>
<Serial>5678</Serial>
</product>
<product>
<ID>456</ID>
<Name>crew</Name>
<Serial>4566</Serial>
</product>
</shirts>
<pants>
<product>
<ID>123</ID>
<Name>jeans</Name>
<Serial>1234</Serial>
<Color>blue</Color>
</product>
<product>
<ID>137</ID>
<Name>skirt</Name>
<Serial>3455</Serial>
<Color>black</Color>
</product>
</pants>
</Dress>
I need convert this XML as:
<Dress ID="001">
<shirts>
<product ID="345" Name="tee" Serial="5678"/>
<product ID="456" Name="crew" Serial="4566"/>
</shirts>
<pants>
<product ID="123" Name="jeans" Serial="1243" Color="blue"/>
<product ID="123" Name="skirt" Serial="3455" Color="black"/>
</pants>
</Dress>
Basically I need to convert the elements to attributes. How do I do this using c#?
Try withe below one. it generates output as you expected.
using System;
using System.Linq;
using System.Xml.Linq;
namespace CangeOneXmlToAnotherXmlConsoleApp
{
class Program
{
static void Main(string[] args)
{
var sourceXml = #"<Dress>
<ID>001</ID>
<shirts>
<product>
<ID>345</ID>
<Name>tee</Name>
<Serial>5678</Serial>
</product>
<product>
<ID>456</ID>
<Name>crew</Name>
<Serial>4566</Serial>
</product>
</shirts>
<pants>
<product>
<ID>123</ID>
<Name>jeans</Name>
<Serial>1234</Serial>
<Color>blue</Color>
</product>
<product>
<ID>137</ID>
<Name>skirt</Name>
<Serial>3455</Serial>
<Color>black</Color>
</product>
</pants>
</Dress>";
var xmlDoc = XDocument.Parse(sourceXml);
//Remove the ID element
var firstChildNodeVal = ((XElement)((XContainer)xmlDoc.FirstNode).FirstNode).Value;
xmlDoc.Descendants("ID").Remove();
//Add an attribute(ID) with value to the root element
xmlDoc.Root.SetAttributeValue("ID", firstChildNodeVal);
//Define the new elements to be available inside the root element
var elemetsToBeFormatted = new string[] { "shirts", "pants" };
//Loop it and add the elements inside root element
foreach (var item in elemetsToBeFormatted)
{
var aitem = xmlDoc.Root.Elements(item).Elements("product").ToList();
aitem.ForEach(p => p.Elements().ToList().ForEach(e => { p.SetAttributeValue(e.Name, e.Value); e.Remove(); }));
}
var expectedXml = xmlDoc.ToString();
Console.WriteLine(expectedXml);
Console.Read();
}
}
}
OUTPUT
private static void ReplaceElementsWithAttributes()
{
string xmlData = #"<Dress>
<ID>001</ID>
<shirts>
<product>
<ID>345</ID>
<Name>tee</Name>
<Serial>5678</Serial>
</product>
<product>
<ID>456</ID>
<Name>crew</Name>
<Serial>4566</Serial>
</product>
</shirts>
<pants>
<product>
<ID>123</ID>
<Name>jeans</Name>
<Serial>1234</Serial>
<Color>blue</Color>
</product>
<product>
<ID>137</ID>
<Name>skirt</Name>
<Serial>3455</Serial>
<Color>black</Color>
</product>
</pants>
</Dress>";
var doc = XDocument.Parse(xmlData);
var replaceElementTargets = new string[] { "shirts", "pants" };
foreach (var target in replaceElementTargets)
{
var product = doc.Root.Elements(target).Elements("product").ToList();
product.ForEach(p => p.Elements().ToList().ForEach(e => { p.SetAttributeValue(e.Name, e.Value); e.Remove(); }));
}
var outputXML = doc.ToString();
}

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

Add XML element before last element using Linq

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

Linq to Xml query to child nodes

<InventoryList>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<Name>Pizza Ristorante Hawaii</Name>
<Price>2.99</Price>
<VariableWeightPrice>€ 8,42 / kg</VariableWeightPrice>
<Brand>Dr.Oetker</Brand>
<PackageInfo>355 GR</PackageInfo>
<categoryString />
<PictureSmallFilename>1small.jpg</PictureSmallFilename>
<InformationTakenFrom>Jumbo</InformationTakenFrom>
<MarketItemUrl></MarketItemUrl>
<BarCode>4001724819608</BarCode>
<IsBlackListed>false</IsBlackListed>
<ItemLists>
<Item>
<ListName>in</ListName>
<Quantity>1</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
<Item>
<ListName>out</ListName>
<Quantity>2</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
</ItemLists>
</Product>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>2</Id>
<Name>Produto 2</Name>
<Price>2.99</Price>
<VariableWeightPrice>€ 5,55 / kg</VariableWeightPrice>
<Brand>Dr.Oetker</Brand>
<PackageInfo>355 GR</PackageInfo>
<categoryString />
<PictureSmallFilename>1small.jpg</PictureSmallFilename>
<InformationTakenFrom>Jumbo</InformationTakenFrom>
<MarketItemUrl></MarketItemUrl>
<BarCode>4001724819608</BarCode>
<IsBlackListed>false</IsBlackListed>
<ItemLists>
<Item>
<ListName>out</ListName>
<Quantity>1</Quantity>
<QuantityWeight>0</QuantityWeight>
</Item>
</ItemLists>
</Product>
</InventoryList>
thanks in advance for your help.
I have this xml database
i want to return all products that have the ListName = "out", but this query i´m trying it´s only returning the second product, and i need it to return the first and the second product.
var _queryItems = from c in xml.Descendants("Product")
where
c.Element("ItemLists").Element("Item").Element("ListName").Value == "out"
select c;
thanks :)
Right now you just check the first Item element, instead you want to check if any Item's ListName matches "out":
var _queryItems = from c in xml.Descendants("Product")
where c.Element("ItemLists")
.Elements("Item").Any( x=> x.Element("ListName").Value == "out")
select c;

Categories

Resources