XSLT处理具有非常宽松的标准XML(EAD)(XSLT to process XML with v

2019-10-16 23:58发布

我一直有一个星期尝试写XSLT代码,可以处理符合(非常宽松)的XML文档的地狱EAD标准 。

一个EAD文档中的有用信息是难以精确定位。 不同的EAD文件可以将相同的信息比特的数据树的完全不同的部分。 另外,一个单一的EAD文档中,相同的标记可以被用于不同的信息的不同位置使用无数次。 对于这样的一个例子,请参阅本SO发布 。 这使得它很难设计出正确处理这些不同的文件的单个XSLT文件。

概括地说,该问题可以被描述为:

  • 我该如何选择是在一个不知名的地点特定EAD节点,
  • 毫无意外地选择具有相同不必要的节点name()

我终于凑齐了我所需要的XSLT,并认为这将是最好的放在此处的代码的通用版本,以便其他人可以从中benifit或改善它。

我很想来标记的“EAD”标签这个问题,但我没有足够的代表。 如果任何人代表适量认为这将是有益的,请这样做。

Answer 1:

首先该解决方案的简短描述,其次是代码。

  1. 检查该EAD文档包含组件(子)记录(与指定的<cXX> 如果没有,我们不必担心重复EAD标签。 该标签仍然可以在任意包装安葬。 为了找到他们,请参阅步骤3。
  2. 如果子记录存在,小心不要处理<dsc>直到其他标记处理标记。 要找到其他标签,请参阅步骤3,则第4步来处理孩子的记录。
  3. 通过与匹配他们和调用模板的各种包装递归apply-template越往下的树中的任何元素节点上。
  4. 我们现在正在处理子记录。 重复步骤2(认真处理解决这一子记录孩子之前的所有其他标签)这样做,那么第4步。

这里是我想出了XSLT代码(通用版):

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>

<xsl:template match="/ead">
<records>
    <xsl:if test="//dsc">
        <!-- if there are <cXX> nodes, we'll handle the main record differently.
             <cXX> nodes are always found in the 'dsc' node, which contains nothing else -->
        <xsl:call-template name="carefully_process"/>
    </xsl:if>
    <xsl:if test="not(//dsc)">
        <record>
            <!-- Just process the existing nodes -->
            <xsl:apply-templates select="*"/>
        </record>
    </xsl:if>
</records>
</xsl:template>

<xsl:template name="carefully_process">
    <!-- first we'll process all the nodes for the main
         record. Then we'll call the child records -->
    <record>
        <!-- have to be careful not to process //archdesc/dsc yet -->
        <xsl:apply-templates select="*[not(self::archdesc)]"/>
        <xsl:apply-templates select="archdesc/*[not(self::dsc)]"/>

    <!-- Now we can close off the master record, -->
    </record>
    <!-- and process the child records -->
    <xsl:apply-templates select="/ead/archdesc/dsc"/>
</xsl:template>

<xsl:template match="dsc">
    <!-- Start processing the child records (we use for-each to get a good position() -->
    <xsl:for-each select="*[starts-with(name(),'c0') or starts-with(name(),'c1') or name() = 'c']">
        <xsl:apply-templates select=".">
            <!-- we pass the unittitle and unitid of the master record, so that child
                 records can be linked to it. We pass the position of the child so that
                 a unitid can be created if it doesn't exist -->
            <xsl:with-param name="partitle" select="normalize-space(/ead/archdesc/did/unittitle)"/>
            <xsl:with-param name="parid" select="normalize-space(/ead/archdesc/did/unitid)"/>
            <xsl:with-param name="pos" select="position()"/>
        </xsl:apply-templates>
    </xsl:for-each>
</xsl:template>

<!-- process child nodes -->
<xsl:template match="*[starts-with(name(),'c0') or starts-with(name(),'c1') or name() = 'c']" >
<xsl:param name="partitle"/>
<xsl:param name="parid"/>
<xsl:param name="pos"/>
    <!-- start this child record -->
    <record>

        <!-- EAD does not require a unitid, but my code does.
             If it doesn't exist, create it -->
        <xsl:if test="not(./did/unitid)">
            <atom name="unitid">
                <xsl:value-of select="$parid"/><xsl:text>-</xsl:text><xsl:value-of select="$pos"/>
            </atom>
        </xsl:if>

        <!-- get the level of this component -->
        <atom name="eadlevel">
            <xsl:value-of select="concat(translate(substring(@level,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'),substring(@level,2))"/>
        </atom>

        <!-- Do *something* to attach this record to it's parent.
             Probably involves $partitle and $parid. For example: -->
        <ref>
            <atom name="unittitle"><xsl:value-of select="$partitle"/></atom>
            <atom name="unitid"><xsl:value-of select="$parid"/></atom>
        </ref>

        <!-- now process all the other nodes -->
        <xsl:apply-templates select="*[not(starts-with(name(),'c0') or starts-with(name(),'c1') or name() = 'c')]"/>

    <!-- finish this child record -->
    </record>

    <!-- prep the variables we'll need for attaching any child records (<cXX+1>) to this record -->
    <xsl:variable name="this_title">
        <xsl:value-of select="normalize-space(./did/unittitle)"/>
    </xsl:variable> 
    <xsl:variable name="this_id">
        <xsl:if test="./did/unitid">
            <xsl:value-of select="./did/unitid"/>
        </xsl:if>
        <xsl:if test="not(./did/unitid)">
            <xsl:value-of select="$parid"/><xsl:text>-</xsl:text><xsl:value-of select="$pos"/>
        </xsl:if>
    </xsl:variable>

    <!-- now process the children of this node -->
    <xsl:for-each select="*[starts-with(name(),'c0') or starts-with(name(),'c1') or name() = 'c']">
        <xsl:apply-templates select=".">
            <xsl:with-param name="partitle" select="$this_title"/>
            <xsl:with-param name="parid" select="$this_id"/>
            <xsl:with-param name="pos" select="position()"/>
        </xsl:apply-templates>
    </xsl:for-each>
</xsl:template>

<!-- these are usually just wrappers. Go one level deeper -->
<xsl:template match="descgrp|eadheader|revisiondesc|filedesc|titlestmt|profiledesc|archdesc|archdescgrp|daogrp|langusage|did|frontmatter">
    <xsl:apply-templates select="*"/>
</xsl:template>

<!-- below this point, add templates for processing specific EAD units
     of information. For example, the template might look like

<xsl:template match="titleproper">
    <atom name="titleproper">
        <xsl:value-of select="normalize-space(.)"/>
    </atom>
</xsl:template>
-->

<!-- instead of having a template for each EAD information unit, consider
     a generic template that handles them all the same way. For example:
-->
<xsl:template match="*">
    <atom>
        <xsl:attribute name="name"><xsl:value-of select="name()"/></xsl:attribute>
        <xsl:value-of select="normalize-space(.)"/>
    </atom>
</xsl:template>

</xsl:stylesheet>


文章来源: XSLT to process XML with very loose standards (EAD)