How to convert XML Elements to Attributes using c# - 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();
}

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

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

Select list of node by innertext. Issue XmlDocument

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

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

Looping through nodes with XPathNavigator

Hi below is a sample of the xml i am using. I been through allsorts of options I can think of to be able to start at the personData node and iterate the results, and nothing i seem to try works unless I navigate manually through each child node from the root down. Can anyone advise on how I can do this without starting at the root
My code currently is
using (var r = File.OpenText(#"C:\S\sp.xml"))
{
XPathDocument document = new XPathDocument(XmlReader.Create(r));
XPathNavigator xPathNav = document.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xPathNav.NameTable);
nsmgr.AddNamespace("g2", "http://person.transferobject.com/xsd");
XPathNodeIterator xni = xPathNav.Select("/g2:companys/g2:company/g2:person/g2:personData", nsmgr);
foreach (XPathNavigator nav in xni)
Console.WriteLine(nav.Name);
}
Xml
<?xml version="1.0" encoding="UTF-8"?>
<Header xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<dataSource xmlns="http://person.transferobject.com/xsd">IG2</dataSource>
<dateTime xmlns="http://person.transferobject.com/xsd">Thu Mar 21 15:56:42 GMT 2013</dateTime>
<formatVersion xmlns="http://person.transferobject.com/xsd">2.0</formatVersion>
<companys xmlns="http://person.transferobject.com/xsd">
<company>
<errorMessages xsi:nil="true"/>
<person>
<personData>
<address>
<address1 xmlns="http://transferobject.com/xsd">37 Smith St</address1>
<county xmlns="http://transferobject.com/xsd">COUNTY-37</county>
<postcode xmlns="http://transferobject.com/xsd">Po12 123</postcode>
</address>
<basicDetails>
<currentFirstName xmlns="http://transferobject.com/xsd">Fred</currentFirstName>
<currentLastName xmlns="http://transferobject.com/xsd">Bloggs</currentLastName >
<currentStage xmlns="http://transferobject.com/xsd">H1</currentStage>
<currentGroup xmlns="http://transferobject.com/xsd">3</currentGroup>
<dob xmlns="http://transferobject.com/xsd">2000-04-25</dob>
<email xmlns="http://transferobject.com/xsd">AN#AN.AOM</email>
<entryDate xmlns="http://transferobject.com/xsd">2003-09-03</entryDate>
</basicDetails>
</personData>
<personData>
<address>
<address1 xmlns="http://transferobject.com/xsd">37 Smith St</address1>
<county xmlns="http://transferobject.com/xsd">COUNTY-37</county>
<postcode xmlns="http://transferobject.com/xsd">Po12 123</postcode>
</address>
<basicDetails>
<currentFirstName xmlns="http://transferobject.com/xsd">John</currentFirstName>
<currentLastName xmlns="http://transferobject.com/xsd">Bloggs</currentLastName >
<currentStage xmlns="http://transferobject.com/xsd">H1</currentStage>
<currentGroup xmlns="http://transferobject.com/xsd">3</currentGroup>
<dob xmlns="http://transferobject.com/xsd">1999-04-25</dob>
<email xmlns="http://transferobject.com/xsd">AN#AN.AOM</email>
<entryDate xmlns="http://transferobject.com/xsd">2003-09-03</entryDate>
</basicDetails>
</personData>
</person>
</company>
</companys>
</header>
I know you're using XPath, but as you have an answer with XPath I'll give one using Linq
using System;
using System.Linq;
using System.Xml.Linq;
namespace xmlTest
{
class Program
{
static void Main()
{
XDocument doc = XDocument.Load("C:\\Users\\me\\Desktop\\so.xml");
var personDataDetails = (from p in doc.Descendants().Elements()
where p.Name.LocalName == "personData"
select p);
foreach (var item in personDataDetails)
{
Console.WriteLine(item.ToString());
}
Console.ReadKey();
}
}
}
Are you just asking how you can iterate through the personData nodes without listing the full path? If that's what you want to do, you can just do this:
XPathNodeIterator xni = xPathNav.Select("//g2:personData", nsmgr);

Categories

Resources