简体   繁体   中英

Bulk Editing Specific Elements in an XML with C#

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM