I have an .xml file that shares an attribute between two different elements. I am trying to multiply the attributes inside one elements with one variable, and multiply the attributes in the other element with a different variable.
<acquirecosts>
<item>
<key>COST_SHOP_DEFAULT</key>
<quantity value="1"/>
<costtype>COST_TYPE_PRICE</costtype>
<items>
<item>
<item>CURRENCY_CASH</item>
<quantity value="6000"/>
</item>
</items>
<unlocks/>
</item>
</acquirecosts>
<sellprices>
<item>
<key>SELL_SHOP_DEFAULT</key>
<quantity value="1"/>
<costtype>COST_TYPE_PRICE</costtype>
<items>
<item>
<item>CURRENCY_CASH</item>
<quantity value="6000"/>
</item>
</items>
<unlocks/>
</item>
</sellprices>
The "CURRENCY_CASH" quantity value inside <./acquirecosts> is being multiplied by 2, and the "CURRENCY_CASH" quantity value inside <./sellprices> is being multiplied by 0.5.
using System;
using System.Xml;
using System.Xml.XPath;
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\Users\Darkye\Desktop\shopprices.xml");
var buyModifier = 2;
var sellModifier = 0.5;
var caItNodesBuy = caNode.XPathSelectElement("./acquirecosts").Elements();
foreach (var caItNodeBuy in caItNodesBuy)
{
var caItNodeItems = caItNodeBuy.XPathSelectElement("./items").Elements();
foreach (var item in caItNodeItems)
{
var caItNodeItemKey = item.Element("item").Value;
if (caItNodeItemKey != "CURRENCY_CASH") continue;
var caItNodeItemValue = (int)Math.Floor((double)int.Parse(item.Element("quantity").Attribute("value").Value) * buyModifier);
item.Element("quantity").SetAttributeValue("value", caItNodeItemValue);
}
caItNodeBuy.XPathSelectElement("./items").ReplaceNodes(caItNodeItems);
}
caNode.XPathSelectElement("./acquirecosts").ReplaceNodes(caItNodesBuy);
var caItNodesSell = caNode.XPathSelectElement("./sellprices").Elements();
foreach (var caItNodeSell in caItNodesSell)
{
var caItNodeItems = caItNodeSell.XPathSelectElement("./items").Elements();
foreach (var item in caItNodeItems)
{
var caItNodeItemKey = item.Element("item").Value;
if (caItNodeItemKey != "CURRENCY_CASH") continue;
var caItNodeItemValue = (int)Math.Floor((double)int.Parse(item.Element("quantity").Attribute("value").Value) * sellModifier);
item.Element("quantity").SetAttributeValue("value", caItNodeItemValue);
}
caItNodeSell.XPathSelectElement("./items").ReplaceNodes(caItNodeItems);
}
caNode.XPathSelectElement("./sellprices").ReplaceNodes(caItNodesSell);
But I am struggling to figure out what and where to introduce "caNode" as. I'm assuming it's a variable, but I'm lost beyond that. When changing caNode to "doc" it just introduces errors on XPathSelectElement. Unless there's an easier way of applying these edits inside specific elements, I'm not sure what else to try.
Please try the following solution.
It is using so called Identity Transform pattern.
It will modify <quantity> element #value attribute value based on the required logic without touching anything else.
Input XML
<root>
<acquirecosts>
<item>
<key>COST_SHOP_DEFAULT</key>
<quantity value="1"/>
<costtype>COST_TYPE_PRICE</costtype>
<items>
<item>
<item>CURRENCY_CASH</item>
<quantity value="6000"/>
</item>
</items>
<unlocks/>
</item>
</acquirecosts>
<sellprices>
<item>
<key>SELL_SHOP_DEFAULT</key>
<quantity value="1"/>
<costtype>COST_TYPE_PRICE</costtype>
<items>
<item>
<item>CURRENCY_CASH</item>
<quantity value="6000"/>
</item>
</items>
<unlocks/>
</item>
</sellprices>
</root>
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:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="quantity">
<xsl:choose>
<xsl:when test="preceding-sibling::*='CURRENCY_CASH'">
<xsl:copy>
<xsl:attribute name="value">
<xsl:if test="ancestor::*[local-name() = 'acquirecosts']">
<xsl:value-of select="#value * 2"/>
</xsl:if>
<xsl:if test="ancestor::*[local-name() = 'sellprices']">
<xsl:value-of select="#value * 0.5"/>
</xsl:if>
</xsl:attribute>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output XML
<root>
<acquirecosts>
<item>
<key>COST_SHOP_DEFAULT</key>
<quantity value="1" />
<costtype>COST_TYPE_PRICE</costtype>
<items>
<item>
<item>CURRENCY_CASH</item>
<quantity value="12000" />
</item>
</items>
<unlocks />
</item>
</acquirecosts>
<sellprices>
<item>
<key>SELL_SHOP_DEFAULT</key>
<quantity value="1" />
<costtype>COST_TYPE_PRICE</costtype>
<items>
<item>
<item>CURRENCY_CASH</item>
<quantity value="3000" />
</item>
</items>
<unlocks />
</item>
</sellprices>
</root>
c#, XSLT transformation
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);
}
}
Use Xml Linq and do two passes
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);
XElement acquiredcosts = doc.Descendants("acquirecosts").FirstOrDefault();
List<XElement> currentCash = acquiredcosts.Descendants("item").Where(x => (string)x.Element("item") == "CURRENCY_CASH").ToList();
foreach (XElement c in currentCash)
{
XElement quantity = c.Element("quantity");
quantity.SetAttributeValue("value", 2 * (int)quantity.Attribute("value"));
}
XElement sellPrices = doc.Descendants("sellprices").FirstOrDefault();
currentCash = sellPrices.Descendants("item").Where(x => (string)x.Element("item") == "CURRENCY_CASH").ToList();
foreach (XElement c in currentCash)
{
XElement quantity = c.Element("quantity");
quantity.SetAttributeValue("value", .5 * (int)quantity.Attribute("value"));
}
}
}
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Hi Friends i need This problem my xml style
<stringtable>
<section>
<string _enum="0x78B5CB94" _extra="0xFFFFFFFF" _id="856571" _value="Anladin.[Timing 0.43 0.00 1]" />
<string _enum="0xA980AB0A" _extra="0xFFFFFFFF" _id="856572" _value="De nada.[Timing 0.37 0.00 1]" />
<_name>0x25C84B25</_name>
</section>
<_language>english</_language>
</stringtable>
How to Convert This Style
<stringtable language='english'>
<section name='0x25C84B25'>
<string enum='0x78B5CB94' extra='0xFFFFFFFF' id='856571' value='Anladın.[Timing 0.43 0.00 1]'></string>
<string enum='0xA980AB0A' extra='0xFFFFFFFF' id='856572' value='De nada.[Timing 0.37 0.00 1]'></string>
</stringtable>
</section>
By using XSLT.
Input XML
<stringtable>
<section>
<string _enum="0x78B5CB94" _extra="0xFFFFFFFF" _id="856571" _value="Anladin.[Timing 0.43 0.00 1]"/>
<string _enum="0xA980AB0A" _extra="0xFFFFFFFF" _id="856572" _value="De nada.[Timing 0.37 0.00 1]"/>
<_name>0x25C84B25</_name>
</section>
<_language>english</_language>
</stringtable>
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:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stringtable">
<xsl:copy>
<xsl:attribute name="language">
<xsl:value-of select="_language"/>
</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="section">
<xsl:copy>
<xsl:attribute name="name">
<xsl:value-of select="_name"/>
</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="_name" />
<xsl:template match="_language" />
</xsl:stylesheet>
Output
<stringtable language="english">
<section name="0x25C84B25">
<string _enum="0x78B5CB94" _extra="0xFFFFFFFF" _id="856571" _value="Anladin.[Timing 0.43 0.00 1]" />
<string _enum="0xA980AB0A" _extra="0xFFFFFFFF" _id="856572" _value="De nada.[Timing 0.37 0.00 1]" />
</section>
</stringtable>
c# XSLT transformation
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);
}
}
I have an XML file with :
<IMG0></IMG0>
<IMG1></IMG1>
<IMG2></IMG2>
It represents up to 10 images.
I try to delete the number for having :
<IMG></IMG>
<IMG></IMG>
<IMG></IMG>
I make :
for (int l = 0; l <= 10; l++)
{
doc.InnerXml.Replace("IMG" + l, "IMG");
}
"doc" is an XMLDocument.
But the node doesn't change.
What can I do ?
By using XSLT and Identity Transform pattern.
The 2nd template will find any XML element that starts with 'IMG' and transform it to just 'IMG' while keeping everyhing else as-is intact.
XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Betalningsmottagares_x0020_postnr>401 01</Betalningsmottagares_x0020_postnr>
<Betalningsmottagares_x0020_postort>Göteborg</Betalningsmottagares_x0020_postort>
<IMG0>10</IMG0>
<IMG1>20</IMG1>
<IMG2>30</IMG2>
</root>
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="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'IMG')]">
<IMG>
<xsl:value-of select="."/>
</IMG>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version='1.0' encoding='utf-8' ?>
<root>
<Betalningsmottagares_x0020_postnr>401 01</Betalningsmottagares_x0020_postnr>
<Betalningsmottagares_x0020_postort>Göteborg</Betalningsmottagares_x0020_postort>
<IMG>10</IMG>10
<IMG>20</IMG>20
<IMG>30</IMG>30
</root>
From C# code I want to delete node from XSLT.
Eg. I have below XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template name="URLSpliter">
<xsl:param name="url" />
<xsl:variable name="splitURL" select="substring - after($url, '/')" />
<xsl:if test="contains($splitURL, '/')">
<xsl:call-template name="URLSpliter">
<xsl:with-param name="url" select="$splitURL" />
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($splitURL, '/'))">
<xsl:value-of select="$splitURL" />
</xsl:if>
</xsl:template>
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
Here I want to delete entire urlsplitter node and all the nodes within URLSplitter
Entire <xsl:template name="URLSpliter"> ...</template> should get deleted (All the nodes within + that particular node )
you can use linq to xml and remove it like as below
documentRoot
.Descendants("template")
.Where(ele=> (string) ele.Attribute("name") == "URLSpliter")
.Remove();
Working sample :
XElement documentRoot =
XElement.Parse (#"<ordersreport date='2012-08-01'>
<returns>
<template name='URLSpliter'>
</template>
<amount>
<orderid>2</orderid>
<orderid>3</orderid>
<orderid>21</orderid>
<orderid>23</orderid>
</amount>
</returns>
</ordersreport>");
documentRoot
.Descendants("template")
.Where(ele=> (string) ele.Attribute("name") == "URLSpliter")
.Remove();
Console.WriteLine(documentRoot.ToString());
This piece of code will work for you. Just replace the path accordingly.
string xsltPath = #"C:\Users\ankushjain\Documents\Visual Studio 2017\Projects\ConsoleApp1\ConsoleApp1\XSLTFile.xslt";
string pathToSave = #"C:\Users\ankushjain\Documents\Visual Studio 2017\Projects\ConsoleApp1\ConsoleApp1\{0}.xslt";
XmlDocument xslDoc = new XmlDocument();
xslDoc.Load(xsltPath);
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xslDoc.NameTable);
namespaceManager.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
var nodesToDelete = xslDoc.SelectNodes("//xsl:template[#name='URLSpliter']", namespaceManager);
if (nodesToDelete != null & nodesToDelete.Count > 0)
{
for (int i = nodesToDelete.Count - 1; i >= 0; i--)
{
nodesToDelete[i].ParentNode.RemoveChild(nodesToDelete[i]);
}
xslDoc.Save(string.Format(pathToSave, Guid.NewGuid()));
}
I want to merge nodes of two xml files in C# or XSLT. If the Path value of Method nodes of two different xml files are same. The two Method nodes should be merged as one in the output.
Example:
File1:
<Methods>
<Method>
<ID>1234</ID>
<Name>manager</Name>
<Path>path1</Path>
</Method>
</Methods>
File2:
<Methods>
<Method>
<Path>path1</Path>
<Description>text</Description>
</Method>
</Methods>
Output:
<Methods>
<Method>
<ID>1234</ID>
<Name>manager</Name>
<Description>text</Description>
</Method>
</Methods>
Try it this way?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="file2" select="document('file2.xml')" />
<xsl:key name="method-by-path" match="Method" use="Path" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Method">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:variable name="path" select="Path"/>
<!-- switch context to the other file -->
<xsl:for-each select="$file2">
<xsl:copy-of select="key('method-by-path', $path)/*[not(self::Path)]" />
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note: this does not check for duplicate nodes.
LINQ to XML is quite effective. Try
var xml1 = XDocument.Load("File1.xml");
var xml2 = XDocument.Load("File2.xml");
foreach (XElement metNode in xml1.Descendants("ID"))
{
metNode.AddAfterSelf(xml2.Descendants("Path").Where(ele => ele.Value.Equals(metNode.Parent.Element("Path").Value)).FirstOrDefault().Parent.Element("Description"));
}
i am trying to parse an xml element using XMLDocument (DItem >> Title)
below is my code but somehow i am not getting hold of it.... any help?
XmlDocument xmldoc = new XmlDocument();
XmlNamespaceManager xmlns = new XmlNamespaceManager(xdoc.NameTable);
xmlns.AddNamespace("DItems", "http://namespace.xsd");
xmldoc.Load(url);
var title = xmldoc.SelectNodes("content", xmlns);
foreach (XmlNode node in title)
{
string title = node.Attributes["Title"].Value;
//this.ddlTitle.Items.Add(new ListItem(title));
}
here is my XML:
<?xml version='1.0'?>
<root xmlns="http://www.w3.org/2005/Atom">
<title type="text">title</title>
<entry>
<content type="application/xml">
<Items xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.namespace.xsd">
<CatalogSource Acronym="ABC" OrganizationName="organization name" />
<Item Id="28466" CatalogUrl="url">
<DItem xmlns:content="http://namespace.xsd" TargetUrl="http://index.html" Title="my title1">
<content:Source Acronym="ABC" OrganizationName="ABC" />
</DItem>
</Item>
</Items>
</content>
</entry>
<entry>
<content type="application/xml">
<Items xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.namespace.xsd">
<CatalogSource Acronym="ABC" OrganizationName="organization name" />
<Item Id="28466" CatalogUrl="url">
<DItem xmlns:content="http://namespace.xsd" TargetUrl="http://index.html" Title="my title2">
<content:Source Acronym="ABC" OrganizationName="ABC" />
</DItem>
</Item>
</Items>
</content>
</entry>
<entry>
<content type="application/xml">
<Items xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.namespace.xsd">
<CatalogSource Acronym="ABC" OrganizationName="organization name" />
<Item Id="28466" CatalogUrl="url">
<DItem xmlns:content="http://namespace.xsd" TargetUrl="http://index.html" Title="my title3">
<content:Source Acronym="ABC" OrganizationName="ABC" />
</DItem>
</Item>
</Items>
</content>
</entry>
</root>
var xmldoc = new XmlDocument();
var xmlns = new XmlNamespaceManager(xmldoc.NameTable);
xmlns.AddNamespace("DItems", "http://www.namespace.xsd");
xmldoc.Load(url);
var titleNodes = xmldoc.SelectNodes("//DItems:DItem/#Title", xmlns);
var result = titleNodes.Cast<XmlAttribute>().Select(a => a.Value).ToList();
Output (list of objects):
my title1
my title2
my title3