如何合并XML文件的特定部分

时间:2018-08-22 15:46:35

标签: c# xml xslt

我有一个XML文件,该文件已从Excel导出,可以满足我的需要。用XML来保存数据是一次操作,除一个问题外,它确实遇到了我的需要。

XML文件包含数千个“任务”条目,每个任务都列出了执行该任务所需的各种耗材。每个任务只能出现一次,尽管其中显然可以列出许多耗材。

问题是从Excel遇到的地方,如果一个任务需要3个耗材,则它会生成3次任务,每个任务只包含一个耗材。

因此,我需要将具有一项常见任务的耗材合并到一个任务中。这将是一次操作,并且数据将从此处开始以XML进行管理。

下面的XML示例:

<tasks>
        <task>
            <taskCode>123456</taskCode>
            <taskName>Mow lawn</taskName>
            <supplies>
                <tool>
                    <id>Edge trimmer</id>
                </tool>
            </supplies>
        </task>
        <task>
            <taskCode>123456</taskCode>
            <taskName>Mow lawn</taskName>
            <supplies>
                <tool>
                    <id>Lawn mover</id>
                </tool>
            </supplies>
        </task>
        <task>
            <taskCode>45678</taskCode>
            <taskName>Paint wall</taskName>
            <supplies>
                <tool>
                    <id>Paint brush</id>
                </tool>
            </supplies>
        </task>
</tasks>

在此示例中,我需要合并前两个任务,以便最终得到:

<tasks>
        <task>
            <taskCode>123456</taskCode>
            <taskName>Mow lawn</taskName>
            <supplies>
                <tool>
                    <id>Edge trimmer</id>
                    <id>Lawn mover</id>
                </tool>
            </supplies>
        </task>
        <task>
            <taskCode>45678</taskCode>
            <taskName>Paint wall</taskName>
            <supplies>
                <tool>
                    <id>Paint brush</id>
                </tool>
            </supplies>
        </task>
</tasks>

3 个答案:

答案 0 :(得分:0)

为原始映射创建类,为新映射创建类,然后反序列化为原始类,为新类创建映射,并将其序列化为新格式。

答案 1 :(得分:0)

Linq to XML允许您进行GROUP BY查询。

var xdoc = XDocument.Parse("YOUR XML DATA");

var tasks = xdoc.Descendants("task")
                .GroupBy
                (
                    t => t.Element("taskCode").Value, // group on taskCode value
                    t => t,
                    (k, g) => new XElement // Make a new "task" element
                    (
                        "task",
                             new XElement("taskCode", k), // with the taskCode
                             g.Select(x => x.Element("taskName")).FirstOrDefault(), // taskName, just pick the first one
                             new XElement("supplies", g.Select(x => x.Element("supplies").Element("tool")).ToList()) // merge the "tools"
                    )
                );

xdoc.Element("tasks").ReplaceNodes(tasks); // then inject updated nodes back in the xml

结果:

<tasks>
    <task>
        <taskCode>123456</taskCode>
        <taskName>Mow lawn</taskName>
        <supplies>
            <tool>
                <id>Edge trimmer</id>
            </tool>
            <tool>
                <id>Lawn mover</id>
            </tool>
        </supplies>
    </task>
    <task>
        <taskCode>45678</taskCode>
        <taskName>Paint wall</taskName>
        <supplies>
            <tool>
                <id>Paint brush</id>
            </tool>
        </supplies>
    </task>
</tasks>

答案 2 :(得分:0)

如果您对XSLT 1.0解决方案感兴趣,则可以使用称为Muenchian分组的技术。当您想按tasktaskCode元素进行分组时,首先要像这样定义一个键

<xsl:key name="tasks" match="task" use="taskCode" />

然后,要获取每个可能的taskCode首次出现的task元素,您可以这样做

<xsl:template match="tasks">
   <xsl:copy>
     <xsl:apply-templates select="task[generate-id() = generate-id(key('tasks', taskCode)[1])]" />
  </xsl:copy>
</xsl:template>

然后,您只需使用键在id下添加所有额外的tool元素。试试这个XSLT:

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

  <xsl:key name="tasks" match="task" use="taskCode" />

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

  <xsl:template match="tasks">
    <xsl:copy>
      <xsl:apply-templates select="task[generate-id() = generate-id(key('tasks', taskCode)[1])]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="tool">
    <xsl:copy>
      <xsl:apply-templates select="key('tasks', ../../taskCode)/supplies/tool/id" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

请注意身份模板的使用,该模板用于复制不需要更改的元素。