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来解决这个问题,但我想知道这是否可能,因为到目前为止,它将清理代码,并允许对单个对象模型进行序列化/反序列化。

实际上,这在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>

需要了解(或阅读)的概念:

  • 身份模板
  • CCD_ 1与Muenchian分组
  • 模板模式

这里有一个XSLT2.0选项,使用xsl:for-each-group而不是muenchian分组。

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>

所以这就是我想到的,我意识到它并不完美。。。如果有人有更好的选择,我很想看到它。

//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()));

相关内容

  • 没有找到相关文章

最新更新