简体   繁体   English

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

[英]How can I merge specific parts of my XML file

I have an XML file which I have exported from Excel to give me what i need. 我有一个XML文件,该文件已从Excel导出,可以满足我的需要。 It was a one time operation to get hold of the data in XML and it has come across exactly how i need apart from one issue. 这是一次一次性获取XML数据的操作,除一个问题外,它确实遇到了我的需要。

The XML file contains thousands of entries for Tasks, each task lists the various supplies required to do that task. XML文件包含数千个“任务”条目,每个任务都列出了执行该任务所需的各种物品。 Each task should only appear once although it can obviously have a number of supplies listed within it. 每个任务只能出现一次,尽管其中显然可以列出许多耗材。

The problem is that where it's come across from Excel, if a task has, say, 3 supplies required, it has generated the task 3 times each one containing only one supply. 问题是从Excel遇到的地方,如果一个任务需要3个耗材,则该任务将生成3次任务,每个任务仅包含一个耗材。

I Therefore need to merge the supplies with a common task into one single task. 因此,我需要将具有一项常见任务的耗材合并到一个任务中。 This will be a one time operation and the data will be managed in XML from here on. 这将是一次操作,并且从现在开始将以XML管理数据。

example XML below : 下面的示例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>

In this example i need the first two tasks merged so that we end up with: 在此示例中,我需要合并前两个任务,以便最终得到:

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

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

Linq to XML allows you to make a GROUP BY query. 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

Results : 结果:

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

If you are interested in the XSLT 1.0 solution, you would use a technique called Muenchian Grouping. 如果您对XSLT 1.0解决方案感兴趣,则可以使用称为Muenchian分组的技术。 As you want to group the task elements by taskCode you first define a key like so 当您想按taskCodetask元素进行taskCode ,首先要定义一个键,如下所示

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

And then, to get the task elements with the first occurrence of each possible taskCode you would do this 然后,要获取每个可能的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>

You then just use the key to add in all the extra id elements under tool . 然后,您只需使用键在tool下添加所有额外的id元素。 Try this XSLT: 试试这个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>

Note the use of the identity template which handles copying the elements you don't need to change. 注意身份模板的使用,该模板用于复制不需要更改的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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