将 xml 文件中的节点值设置为等于 C# 中节点的另一个值

Set the value of a node in xml file to be equal to another value of node in C#

假设我有以下 XML:

<?xml version="1.0" encoding="UTF-8" ?>

<exchange xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://download.autodesk.com/us/navisworks/schemas/nw-exchange-12.0.xsd" units="ft" filename="" filepath="">
  <batchtest name="3636" internal_name="3636" units="ft">
    <clashtests>
      <clashtest name="Ducts VS Ducts" test_type="hard" status="new" tolerance="0.0000000000" merge_composites="0">
        <linkage mode="none"/>
        <left>
          <clashselection selfintersect="0" primtypes="1">
            <locator>/</locator>
          </clashselection>
        </left>
        <right>
          <clashselection selfintersect="0" primtypes="1">
            <locator>/</locator>
          </clashselection>
        </right>
        <rules/>
      </clashtest>
      <clashtest name="Ducts VS Cable Trays" test_type="hard" status="new" tolerance="0.0000000000" merge_composites="0">
        <linkage mode="none"/>
        <left>
          <clashselection selfintersect="0" primtypes="1">
            <locator>/</locator>
          </clashselection>
        </left>
        <right>
          <clashselection selfintersect="0" primtypes="1">
            <locator>/</locator>
          </clashselection>
        </right>
        <rules/>
      </clashtest>
      
      </batchtest>
</exchange>

我想在 Visual Studio

中使用 C# 像这张图片一样

我想将 locator 节点的值更改为取决于 clash test 节点的值,如图所示。

使用xml linq :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            foreach (XElement clashtest in doc.Descendants("clashtest"))
            {
                string name = (string)clashtest.Attribute("name");
                string pattern = "(?'name1'.*)VS(?'name2'.*)";
                Match match = Regex.Match(name, pattern);
                string name1 = match.Groups["name1"].Value.Trim();
                string name2 = match.Groups["name2"].Value.Trim();

                List<XElement> locators = clashtest.Descendants("locator").ToList();
                locators[0].SetValue(name1);
                locators[1].SetValue(name2);
            }
        }
    }
}
var xml = XElement.Load("test.xml");
var clashtests = xml.Element("batchtest").Element("clashtests").Elements("clashtest");

foreach (var clashtest in clashtests)
{
    string name = clashtest.Attribute("name").Value;
    var values = name.Split(new[] { " VS " }, StringSplitOptions.None);

    var left = clashtest.Element("left").Element("clashselection").Element("locator");
    left.Value = values[0];

    var right = clashtest.Element("right").Element("clashselection").Element("locator");
    right.Value = values[1];
}
Console.WriteLine(xml);

指定完整路径:Element(...).Element(...)Descendants 快。它对大型文档可能很重要。

XSLT 具有所谓的身份转换模式。

有用link:XSL Identity Transforms

下面的 XSLT 将按原样复制整个输入 XML,locator 元素除外。一旦找到 locator 元素,它将被替换为新的所需值。

XSLT 函数 substring-before()substring-after() 可以轻松检索 name 属性值的所需部分。

您所要做的只是从您的 C# 代码中调用 XSLT 转换。

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" method="xml" encoding="utf-8"/>

    <!-- IdentityTransform -->
    <xsl:template match="/ | @* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="locator[parent::clashselection/parent::left]">
        <locator>
            <xsl:value-of select="substring-before(ancestor::clashtest/@name, ' VS')"/>
        </locator>
    </xsl:template>

    <xsl:template match="locator[parent::clashselection/parent::right]">
        <locator>
            <xsl:value-of select="substring-after(ancestor::clashtest/@name, 'VS ')"/>
        </locator>
    </xsl:template>
</xsl:stylesheet>