我一直在为这种转换而挣扎,也许,除了我没有太多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()));