Linq或XSLT将多个变量元素合并为一个

时间:2014-04-15 21:00:29

标签: xml linq xslt

我一直在努力应对这种转变,也许,除了我没有多少xslt练习之外,这里的某些人可以解决一些问题。使用xml

<?xml version="1.0" encoding="utf-8"?>
    <recipes version="1.0">
    <name language="en">195P 290000
        000015 000010</name>
     <recipe size="small">
    <subsequence name="START">
      <TMP> 90</TMP>
      <DV> 0</DV>
      <LG> 00FF0000</LG>
      <CB> 0000FF00</CB>
      <TS> 000000FF</TS>
      <BS> 00320A00</BS>
    </subsequence>
   <subsequence name="FR">
      <FWV>453</FWV>
      <RWV>232</RWV>
      <AP>21</AP>
    </subsequence> 
  </recipe>
  <recipe size="medium">
    <subsequence name="START">
      <TMP>215</TMP>
      <DV> 0</DV>
      <LG> 00060000</LG>
      <CB> 00969696</CB>
      <TS> 00191919</TS>
      <BS> 00060606</BS>
    </subsequence>
    <subsequence name="FR">
      <FWV>357</FWV>
      <RWV>0</RWV>
      <AP>0</AP>
    </subsequence>   
     <subsequence name="VC">
      <PS>29</PS>
      <TM>5</TM>
      <AP>0</AP>
    </subsequence>
    <subsequence name="FR">
      <FWV> 0</FWV>
      <RWV>45</RWV>
      <AP>15</AP>
    </subsequence>
       <subsequence name="PG">
      <PS>20</PS>
      <TM>27</TM>
      <LG>00060000</LG>
      <CB>00040404</CB>
      <TS>00040404</TS>
      <BS>00202020</BS>
    </subsequence>
    <subsequence name="BO">
      <BT>4</BT>
    </subsequence>
 </recipe>
   <recipe size="large">
   <subsequence name="FR">
      <FWV>33357</FWV>
      <RWV>0</RWV>
      <AP>0</AP>
    </subsequence>   
     <subsequence name="VC">
      <PS>29</PS>
      <TM>5</TM>
      <AP>0</AP>
    </subsequence>
    <subsequence name="FR">
      <FWV> 2222</FWV>
      <RWV>333</RWV>
      <AP>15</AP>
    </subsequence>
       <subsequence name="PG">
      <PS>20</PS>
      <TM>27</TM>
      <LG>00060000</LG>
      <CB>00040404</CB>
      <TS>00040404</TS>
      <BS>00202020</BS>
    </subsequence>
    <subsequence name="BO">
      <BT>4</BT>
    </subsequence>
 </recipe>
</recipes>

你可以说每个大小都有一个开始序列,但除此之外,它是一个变量子序列,其中FR,VC,PR,BT属性以任何顺序发生。我无法想象的是如何将变换变成像

这样的变形
<?xml version="1.0" encoding="utf-8"?>
    <recipes version="1.0">
    <name language="en">195P 290000 000015 000010</name>
    <subsequence name="START">
      <small>
        <TMP> 90</TMP>
        <DV> 0</DV>
        <LG> 00FF0000</LG>
        <CB> 0000FF00</CB>
        <TS> 000000FF</TS>
        <BS> 00320A00</BS>
      </small>
      <medium>
        <TMP>215</TMP>
        <DV> 0</DV>
        <LG> 00060000</LG>
        <CB> 00969696</CB>
        <TS> 00191919</TS>
        <BS> 00060606</BS>
      </medium>
      <large>
        ..values
      </large>
    </subsequence>
    <subsequence name="FR">
      <small>
       ...values
      </small>
      <medium>
          ..values
      </medium>
    <large>
         ..values
    </large>
    </subsequence>
    <subsequence name="VC">
      <small>
       ...values (again if any
      </small>
      <medium>
          ..values
      </medium>
     <large>
         ..values
    </large>
    </subsequence>
      <subsequence name="FR">
      <small>
       ...values
      </small>
      <medium>
          ..values
      </medium>
    <large>
         ..values
    </large>
    </subsequence>
     ..and so on
    </recipes>

当然,开始很简单,但在那之后我就失去了理智。我需要组合下一个兄弟值(如果它存在)并且与文档顺序中的其他兄弟值相同。更多如果 大订单是FR VC PG和中等订单是FR VC PG我会将它们组合在一起如果它们不相同我会为值引入零。 IE如果第一个大的是FR而且第一个大的是VC,我会以任何顺序取一个,因为无论如何都会有零。

我已经放弃尝试使用XSLT来解决这个问题,但是想知道它是否有可能,因为它将到目前为止清理代码并允许单个对象模型被序列化/反序列化。

3 个答案:

答案 0 :(得分:0)

实际上在XSLT中相对容易。

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

  <xsl:key name="kSubsequenceByName" match="subsequence" use="@name" />

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

  <xsl:template match="recipe">
    <xsl:apply-templates select="node()" />
  </xsl:template>

  <xsl:template match="subsequence">
    <xsl:variable name="myGroup" select="key('kSubsequenceByName', @name)" />
    <xsl:if test="generate-id() = generate-id($myGroup)">
      <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates select="$myGroup" mode="translate" />
      </xsl:copy>
    </xsl:if>
  </xsl:template>

  <xsl:template match="subsequence" mode="translate">
    <xsl:element name="{../@size}">
      <xsl:apply-templates select="node()" />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

为您提供输入样本:

<recipes version="1.0">
  <name language="en">195P 290000
        000015 000010</name>
  <subsequence name="START">
    <small>
      <TMP>90</TMP>
      <DV>0</DV>
      <LG>00FF0000</LG>
      <CB>0000FF00</CB>
      <TS>000000FF</TS>
      <BS>00320A00</BS>
    </small>
    <medium>
      <TMP>215</TMP>
      <DV>0</DV>
      <LG>00060000</LG>
      <CB>00969696</CB>
      <TS>00191919</TS>
      <BS>00060606</BS>
    </medium>
  </subsequence>
  <subsequence name="FR">
    <medium>
      <FWV>357</FWV>
      <RWV>0</RWV>
      <AP>0</AP>
    </medium>
    <medium>
      <FWV>0</FWV>
      <RWV>45</RWV>
      <AP>15</AP>
    </medium>
  </subsequence>
  <subsequence name="VC">
    <medium>
      <PS>29</PS>
      <TM>5</TM>
      <AP>0</AP>
    </medium>
  </subsequence>
  <subsequence name="PG">
    <medium>
      <PS>20</PS>
      <TM>27</TM>
      <LG>00060000</LG>
      <CB>00040404</CB>
      <TS>00040404</TS>
      <BS>00202020</BS>
    </medium>
  </subsequence>
  <subsequence name="BO">
    <medium>
      <BT>4</BT>
    </medium>
  </subsequence>
  .
  .
  .
  .
</recipes>

要知道(或阅读)的概念:

  • 身份模板
  • <xsl:key>和Muenchian分组
  • 模板模式

答案 1 :(得分:0)

这是使用xsl:for-each-group而不是muenchian分组的XSLT 2.0选项。

XML输入(已修改为格式正确)

<recipes version="1.0">
    <name language="en">195P 290000
        000015 000010</name>
    <recipe size="small">
        <subsequence name="START">
            <TMP> 90</TMP>
            <DV> 0</DV>
            <LG> 00FF0000</LG>
            <CB> 0000FF00</CB>
            <TS> 000000FF</TS>
            <BS> 00320A00</BS>
        </subsequence>
    </recipe>
    <recipe size="medium">
        <subsequence name="START">
            <TMP>215</TMP>
            <DV> 0</DV>
            <LG> 00060000</LG>
            <CB> 00969696</CB>
            <TS> 00191919</TS>
            <BS> 00060606</BS>
        </subsequence>
        <subsequence name="FR">
            <FWV>357</FWV>
            <RWV>0</RWV>
            <AP>0</AP>
        </subsequence>   
        <subsequence name="VC">
            <PS>29</PS>
            <TM>5</TM>
            <AP>0</AP>
        </subsequence>
        <subsequence name="FR">
            <FWV> 0</FWV>
            <RWV>45</RWV>
            <AP>15</AP>
        </subsequence>
        <subsequence name="PG">
            <PS>20</PS>
            <TM>27</TM>
            <LG>00060000</LG>
            <CB>00040404</CB>
            <TS>00040404</TS>
            <BS>00202020</BS>
        </subsequence>
        <subsequence name="BO">
            <BT>4</BT>
        </subsequence>
    </recipe>
    <recipe size="large">
        .
        .
        .
        .
    </recipe>
</recipes>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node() except recipe"/>
            <xsl:for-each-group select="recipe/subsequence" group-by="@name">
                <xsl:variable name="name" select="current-grouping-key()"/>
                <xsl:copy>
                    <xsl:apply-templates select="@*"/>
                    <xsl:for-each-group select="/*/recipe[subsequence/@name=$name]" group-by="@size">
                        <xsl:element name="{current-grouping-key()}">
                            <xsl:apply-templates select="subsequence[@name=$name and not(preceding-sibling::subsequence/@name=$name)]/*"/>
                        </xsl:element>                        
                    </xsl:for-each-group>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>        
    </xsl:template>

</xsl:stylesheet>

XML输出

<recipes version="1.0">
   <name language="en">195P 290000
        000015 000010</name>
   <subsequence name="START">
      <small>
         <TMP> 90</TMP>
         <DV> 0</DV>
         <LG> 00FF0000</LG>
         <CB> 0000FF00</CB>
         <TS> 000000FF</TS>
         <BS> 00320A00</BS>
      </small>
      <medium>
         <TMP>215</TMP>
         <DV> 0</DV>
         <LG> 00060000</LG>
         <CB> 00969696</CB>
         <TS> 00191919</TS>
         <BS> 00060606</BS>
      </medium>
   </subsequence>
   <subsequence name="FR">
      <medium>
         <FWV>357</FWV>
         <RWV>0</RWV>
         <AP>0</AP>
      </medium>
   </subsequence>
   <subsequence name="VC">
      <medium>
         <PS>29</PS>
         <TM>5</TM>
         <AP>0</AP>
      </medium>
   </subsequence>
   <subsequence name="PG">
      <medium>
         <PS>20</PS>
         <TM>27</TM>
         <LG>00060000</LG>
         <CB>00040404</CB>
         <TS>00040404</TS>
         <BS>00202020</BS>
      </medium>
   </subsequence>
   <subsequence name="BO">
      <medium>
         <BT>4</BT>
      </medium>
   </subsequence>
</recipes>

答案 2 :(得分:0)

所以这就是我想出来的,我知道它并不完美......如果有人有更好的选择,我很乐意看到它。

//function for applying annotations similar to MSDN example (shame on me)
static XElement XForm(XElement source)
{

    if (source.Annotation<XAttribute>() != null)
    {
        XAttribute anno = source.Annotation<XAttribute>();
        return new XElement( source.Name, anno, source.Attributes(), source.Nodes());
    }
    else
    {
        return new XElement(source.Name,
            source.Attributes(),
            source.Nodes().Select(n =>
                {
                    XElement el = n as XElement;
                    if (el == null)
                        return n;
                    else
                        return XForm(el);
                }
                )
        );
    }
}

XElement root = XElement.Load(".xml");
root.DescendantNodes().OfType<XComment>().Remove();

int subIncrement = 0;

foreach (var el in root.Descendants("subsequence"))
{
    if (el.Attribute("name").Value == "START") { subIncrement = 0; }
    el.AddAnnotation(new XAttribute("key", el.FirstAttribute.Value + subIncrement));
    subIncrement += 1;
}

XElement newRoot = XForm(root);

var transform = new XElement("recipe", from data in
                                    (from seq in newRoot.Descendants("subsequence")
                                     select new XElement(seq.Attribute("key").Value
                                                        ,new XElement(seq.Ancestors().First().FirstAttribute.Value
                                                                     ,seq.Nodes()
                                                                     )
                                                        )
                                    )
                               group data by data.Name.ToString() into groups
                              select new XElement((string)groups.Key , groups.Nodes()));