如何使用 xslt 转换将多个子元素添加到新的父元素中
how to add multiple subelements into new parent element with xslt transform
我在 C# 中使用 ExtendedXmlSerializer,一个已知的限制是添加序列化对象列表。当这样做时,它会产生一个单独的元素(列表本身),其中包含列表的项目。
由于我正在反序列化来自单独应用程序的外部 xml,因此我无法控制 xml 的布局,因此需要在 C# 中反序列化之前转换 xml。
我在 Whosebug 的其他地方找到的转换在将单一类型的元素添加到新的(不存在的)元素中时工作正常,但在尝试使用多种不同类型时失败。
我已尝试尽可能简化源代码xml:
<?xml version="1.0" encoding="utf-8"?>
<Document>
<Engineering version="V16" /> <!-- some other elements besides Engineering as well -->
<SW.Blocks.FB ID="0">
<AttributeList>
<AutoNumber>true</AutoNumber>
<!--more elements including where "Member" is eventually -->
</AttributeList>
<ObjectList>
<MultilingualText ID="1" CompositionName="Comment"/>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits"/>
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits"/>
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits"/>
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits"/>
<MultilingualText ID="71" CompositionName="Title"/>
</ObjectList>
</SW.Blocks.FB>
</Document>
首选输出如下:
<Document>
<Engineering version="V16" />
<!-- some other elements besides Engineering as well -->
<SW.Blocks.FB ID="0">
<AttributeList>
<AutoNumber>true</AutoNumber>
<!--more elements including where "Member" is eventually -->
</AttributeList>
<ObjectList>
<CompileUnitToRemove>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits" />
</CompileUnitToRemove>
<MultilingualTextToRemove>
<MultilingualText ID="1" CompositionName="Comment" />
<MultilingualText ID="71" CompositionName="Title" />
</MultilingualTextToRemove>
</ObjectList>
</SW.Blocks.FB>
</Document>
就像我上面说的,元素 CompileUnitToRemove 和 MultilingualTextToRemove 可以使 ExtendedXmlSerializer 与列表一起工作。
我目前的xslt如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>
<!-- copy all elements over, except what falls into other template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- if local name (namespace independent) is 'Member': -->
<xsl:template match="*[local-name(.)='Member']">
<!-- store namespace of Member in variable-->
<xsl:variable name="ns" select="namespace-uri()" />
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::*[local-name(.)='SubElement'])]"/>
<!-- copy everything (except Subelements) over and create a new element "SubElements" with same nameSpace as parent (Member) -->
<xsl:element name="SubElements" namespace="{$ns}">
<!-- copy all Subelements over into new element -->
<xsl:apply-templates select="*[local-name(.)='SubElement']"/>
</xsl:element>
</xsl:copy>
</xsl:template>
<!-- works the same as above, but trying with 2 items within ObjectList: -->
<xsl:template match="*[local-name(.)='ObjectList']">
<xsl:variable name="ns" select="namespace-uri()" />
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::*[local-name(.)='SW.Blocks.CompileUnit'])]"/>
<xsl:element name="CompileUnitToRemove" namespace="{$ns}">
<xsl:apply-templates select="*[local-name(.)='SW.Blocks.CompileUnit']"/>
</xsl:element>
<xsl:apply-templates select="@*|node()[not(self::*[local-name(.)='MultilingualText'])]"/>
<xsl:element name="MultilingualTextToRemove" namespace="{$ns}">
<xsl:apply-templates select="*[local-name(.)='MultilingualText']"/>
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
但是,这会创建以下 xml:
<Document>
<Engineering version="V16" />
<!-- some other elements besides Engineering as well -->
<SW.Blocks.FB ID="0">
<AttributeList>
<AutoNumber>true</AutoNumber>
<!--more elements including where "Member" is eventually -->
</AttributeList>
<ObjectList>
<MultilingualText ID="1" CompositionName="Comment" />
<MultilingualText ID="71" CompositionName="Title" />
<CompileUnitToRemove>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits" />
</CompileUnitToRemove>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits" />
<MultilingualTextToRemove>
<MultilingualText ID="1" CompositionName="Comment" />
<MultilingualText ID="71" CompositionName="Title" />
</MultilingualTextToRemove>
</ObjectList>
</SW.Blocks.FB>
</Document>
如您所见,其中的元素是重复的。
我也试过将“xsl:apply-templates”部分放在它自己的“xsl:copy”部分,以及它自己的“" 但它要么导致更多重复,要么只是跳过第一个列出的。
如果有人能帮我弄清楚正确的 xslt 布局需要是什么,我将不胜感激。
对于 ExtendedXmlSerializer 列表问题,我也可以使用另一种解决方法,但出于其他原因我确实需要 ExtendedXmlSerializer 并且使用 xslt 是开发人员推荐的解决方案。
以下模板是否满足您的要求?
<xsl:template match="ObjectList">
<ObjectList>
<CompileUnitToRemove>
<xsl:apply-templates select="SW.Blocks.CompileUnit"/>
</CompileUnitToRemove>
<MultilingualTextToRemove>
<xsl:apply-templates select="MultilingualText"/>
</MultilingualTextToRemove>
</ObjectList>
</xsl:template>
我在 C# 中使用 ExtendedXmlSerializer,一个已知的限制是添加序列化对象列表。当这样做时,它会产生一个单独的元素(列表本身),其中包含列表的项目。 由于我正在反序列化来自单独应用程序的外部 xml,因此我无法控制 xml 的布局,因此需要在 C# 中反序列化之前转换 xml。
我在 Whosebug 的其他地方找到的转换在将单一类型的元素添加到新的(不存在的)元素中时工作正常,但在尝试使用多种不同类型时失败。
我已尝试尽可能简化源代码xml:
<?xml version="1.0" encoding="utf-8"?>
<Document>
<Engineering version="V16" /> <!-- some other elements besides Engineering as well -->
<SW.Blocks.FB ID="0">
<AttributeList>
<AutoNumber>true</AutoNumber>
<!--more elements including where "Member" is eventually -->
</AttributeList>
<ObjectList>
<MultilingualText ID="1" CompositionName="Comment"/>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits"/>
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits"/>
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits"/>
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits"/>
<MultilingualText ID="71" CompositionName="Title"/>
</ObjectList>
</SW.Blocks.FB>
</Document>
首选输出如下:
<Document>
<Engineering version="V16" />
<!-- some other elements besides Engineering as well -->
<SW.Blocks.FB ID="0">
<AttributeList>
<AutoNumber>true</AutoNumber>
<!--more elements including where "Member" is eventually -->
</AttributeList>
<ObjectList>
<CompileUnitToRemove>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits" />
</CompileUnitToRemove>
<MultilingualTextToRemove>
<MultilingualText ID="1" CompositionName="Comment" />
<MultilingualText ID="71" CompositionName="Title" />
</MultilingualTextToRemove>
</ObjectList>
</SW.Blocks.FB>
</Document>
就像我上面说的,元素 CompileUnitToRemove 和 MultilingualTextToRemove 可以使 ExtendedXmlSerializer 与列表一起工作。
我目前的xslt如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>
<!-- copy all elements over, except what falls into other template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- if local name (namespace independent) is 'Member': -->
<xsl:template match="*[local-name(.)='Member']">
<!-- store namespace of Member in variable-->
<xsl:variable name="ns" select="namespace-uri()" />
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::*[local-name(.)='SubElement'])]"/>
<!-- copy everything (except Subelements) over and create a new element "SubElements" with same nameSpace as parent (Member) -->
<xsl:element name="SubElements" namespace="{$ns}">
<!-- copy all Subelements over into new element -->
<xsl:apply-templates select="*[local-name(.)='SubElement']"/>
</xsl:element>
</xsl:copy>
</xsl:template>
<!-- works the same as above, but trying with 2 items within ObjectList: -->
<xsl:template match="*[local-name(.)='ObjectList']">
<xsl:variable name="ns" select="namespace-uri()" />
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::*[local-name(.)='SW.Blocks.CompileUnit'])]"/>
<xsl:element name="CompileUnitToRemove" namespace="{$ns}">
<xsl:apply-templates select="*[local-name(.)='SW.Blocks.CompileUnit']"/>
</xsl:element>
<xsl:apply-templates select="@*|node()[not(self::*[local-name(.)='MultilingualText'])]"/>
<xsl:element name="MultilingualTextToRemove" namespace="{$ns}">
<xsl:apply-templates select="*[local-name(.)='MultilingualText']"/>
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
但是,这会创建以下 xml:
<Document>
<Engineering version="V16" />
<!-- some other elements besides Engineering as well -->
<SW.Blocks.FB ID="0">
<AttributeList>
<AutoNumber>true</AutoNumber>
<!--more elements including where "Member" is eventually -->
</AttributeList>
<ObjectList>
<MultilingualText ID="1" CompositionName="Comment" />
<MultilingualText ID="71" CompositionName="Title" />
<CompileUnitToRemove>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits" />
</CompileUnitToRemove>
<SW.Blocks.CompileUnit ID="D" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="26" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="3F" CompositionName="CompileUnits" />
<SW.Blocks.CompileUnit ID="58" CompositionName="CompileUnits" />
<MultilingualTextToRemove>
<MultilingualText ID="1" CompositionName="Comment" />
<MultilingualText ID="71" CompositionName="Title" />
</MultilingualTextToRemove>
</ObjectList>
</SW.Blocks.FB>
</Document>
如您所见,其中的元素是重复的。
我也试过将“xsl:apply-templates”部分放在它自己的“xsl:copy”部分,以及它自己的“
如果有人能帮我弄清楚正确的 xslt 布局需要是什么,我将不胜感激。 对于 ExtendedXmlSerializer 列表问题,我也可以使用另一种解决方法,但出于其他原因我确实需要 ExtendedXmlSerializer 并且使用 xslt 是开发人员推荐的解决方案。
以下模板是否满足您的要求?
<xsl:template match="ObjectList">
<ObjectList>
<CompileUnitToRemove>
<xsl:apply-templates select="SW.Blocks.CompileUnit"/>
</CompileUnitToRemove>
<MultilingualTextToRemove>
<xsl:apply-templates select="MultilingualText"/>
</MultilingualTextToRemove>
</ObjectList>
</xsl:template>