使用XSLT转换动态XML

时间:2015-07-30 06:39:30

标签: xml xslt

我是XSLT的新手,并且已经设法使用XML,其中每行重复节点。我已经获得了一些导入的XML,每个患者记录都有不同的元素,例如:

<?xml version="1.0" encoding="utf-8"?>
<data>
    <patient>
        <link_id>123</link_id>
        <diagnoses>
            <diabetes_type2>
                <diabetes_type2_active>True</diabetes_type2_active>
                <diabetes_type2_description>diabetes mellitus</diabetes_type2_description>
                <diabetes_type2_diagnosis_date>06051999</diabetes_type2_diagnosis_date>
            </diabetes_type2>
        </diagnoses>
    </patient>
    <patient>
        <link_id>456</link_id>
        <diagnoses>
            <chd>
                <chd_active>True</chd_active>
                <chd_description>ischaemic heart disease</chd_description>
                <chd_diagnosis_date>05071997</chd_diagnosis_date>
            </chd>
            <coad>
                <coad_active>True</coad_active>
                <coad_description>chronic obstructive airways disease</coad_description>
                <coad_diagnosis_date>28011986</coad_diagnosis_date>
            </coad>
            <depression>
                <depression_active>True</depression_active>
                <depression_description>depression</depression_description>
                <depression_diagnosis_date>28011986</depression_diagnosis_date>
            </depression>
            <myocardial_infarction>
                <myocardial_infarction_active>True</myocardial_infarction_active>
                <myocardial_infarction_description>myocardial infarction</myocardial_infarction_description>
                <myocardial_infarction_diagnosis_date>05071997</myocardial_infarction_diagnosis_date>
            </myocardial_infarction>
            <osteoarthritis>
                <osteoarthritis_active>True</osteoarthritis_active>
                <osteoarthritis_description>osteoarthritis of the knee</osteoarthritis_description>
                <osteoarthritis_diagnosis_date>28011986</osteoarthritis_diagnosis_date>
            </osteoarthritis>
            <stroke>
                <stroke_active>True</stroke_active>
                <stroke_description>cerebrovascular accident</stroke_description>
                <stroke_diagnosis_date>01011996</stroke_diagnosis_date>
            </stroke>
        </diagnoses>
    </patient>
</data>

我需要导入诊断值,但我不想硬编码可能出现的所有数百个可能的值。我希望有一种方法可以动态引用这些,无论它们的元素名称如何。我通常会使用这样的东西:

    <xsl:for-each select="./data/patient/diagnoses">
            <ROW MODID="" RECORDID="">
                <COL>
                    <DATA>
                        <xsl:value-of select="../../link_id"/>
                    </DATA>
                </COL>
                <COL>
                    <DATA>
                        <xsl:value-of select="./type"/>
                    </DATA>
                </COL>
                <COL>
                    <DATA>
                        <xsl:value-of select="./description"/>
                    </DATA>
                </COL>
                <COL>
                    <DATA>
                        <xsl:value-of select="./active"/>
                    </DATA>
                </COL>
                <COL>
                    <DATA>
                        <xsl:value-of select="./diagnosis_date"/>
                    </DATA>
                </COL>
            </ROW>
        </xsl:for-each>

但不确定如何修改我正在使用的动态元素。

3 个答案:

答案 0 :(得分:1)

您可以使用*引用任何名称的元素,并使用name()local-name()函数动态获取元素名称,例如:

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

    <xsl:template match="/">
      <TABLE>
        <xsl:for-each select="./data/patient/diagnoses/*">
          <xsl:variable name="diagnose" select="name()"/>
          <ROW MODID="" RECORDID="">
            <COL>
              <DATA>
                <xsl:value-of select="../../link_id"/>
              </DATA>
            </COL>
            <COL>
              <DATA>
                <xsl:value-of select="./*[name()=concat($diagnose, '_description')]"/>
              </DATA>
            </COL>
            <COL>
              <DATA>
                <xsl:value-of select="./*[name()=concat($diagnose, '_active')]"/>
              </DATA>
            </COL>
            <COL>
              <DATA>
                <xsl:value-of select="./*[name()=concat($diagnose, '_diagnosis_date')]"/>
              </DATA>
            </COL>
          </ROW>
        </xsl:for-each>
      </TABLE>
    </xsl:template>
</xsl:stylesheet>

<强> Xsltransform.net Demo

答案 1 :(得分:0)

怎么样.... (这是OP接受的更好,更简单的解决方案!)

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

<xsl:template match="data">
  <TABLE>
    <HEADER>
      <COL><DATA>Link</DATA></COL>
      <COL><DATA>Type</DATA></COL>
      <COL><DATA>active</DATA></COL>
      <COL><DATA>description</DATA></COL>
      <COL><DATA>diagnosis_date</DATA></COL>
    </HEADER>
    <xsl:apply-templates select="patient/diagnoses/*" />
  </TABLE>
</xsl:template>

<xsl:template match="diagnoses/*">
  <ROW>
    <COL><DATA><xsl:value-of select="../../link_id" /></DATA></COL>
    <COL><DATA><xsl:value-of select="local-name()" /></DATA></COL>
    <xsl:apply-templates />    
  </ROW>
</xsl:template>

<xsl:template match="*">
  <COL><DATA><xsl:value-of select="." /></DATA></COL>
</xsl:template>  

</xsl:stylesheet>

当应用于您提供的输入文档时,此转换会产生输出...

<table>
    <header>
        <col>
            <data>Link</data>
        </col>
        <col>
            <data>Type</data>
        </col>
        <col>
            <data>active</data>
        </col>
        <col>
            <data>description</data>
        </col>
        <col>
            <data>diagnosis_date</data>
        </col>
    </header>
    <row>
        <col>
            <data>123</data>
        </col>
        <col>
            <data>diabetes_type2</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>diabetes mellitus</data>
        </col>
        <col>
            <data>06051999</data>
        </col>
    </row>
    <row>
        <col>
            <data>456</data>
        </col>
        <col>
            <data>chd</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>ischaemic heart disease</data>
        </col>
        <col>
            <data>05071997</data>
        </col>
    </row>
    <row>
        <col>
            <data>456</data>
        </col>
        <col>
            <data>coad</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>chronic obstructive airways disease</data>
        </col>
        <col>
            <data>28011986</data>
        </col>
    </row>
    <row>
        <col>
            <data>456</data>
        </col>
        <col>
            <data>depression</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>depression</data>
        </col>
        <col>
            <data>28011986</data>
        </col>
    </row>
    <row>
        <col>
            <data>456</data>
        </col>
        <col>
            <data>myocardial_infarction</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>myocardial infarction</data>
        </col>
        <col>
            <data>05071997</data>
        </col>
    </row>
    <row>
        <col>
            <data>456</data>
        </col>
        <col>
            <data>osteoarthritis</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>osteoarthritis of the knee</data>
        </col>
        <col>
            <data>28011986</data>
        </col>
    </row>
    <row>
        <col>
            <data>456</data>
        </col>
        <col>
            <data>stroke</data>
        </col>
        <col>
            <data>True</data>
        </col>
        <col>
            <data>cerebrovascular accident</data>
        </col>
        <col>
            <data>01011996</data>
        </col>
    </row>
</table>

替代版本

在上面我假设active,description和diagnosis_date元素是固定的并且是固定的。如果输入文档可以具有可变范围的属性(例如/ data / patient / diagnoses / coad / coad_myproperty)或~_active,~_ description,~_diagnosis_date属性不是固定顺序,那么请尝试此替代(更动态)版本...

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

<xsl:variable name="fields" select="
  distinct-values(
    /data/patient/diagnoses/*/*
      [starts-with( local-name(), concat( local-name(..),'_'))]
      /substring(   local-name(), string-length( local-name(..))+2))"
  />

<xsl:template match="data">
  <TABLE>
    <HEADER>
      <COL><DATA>Link</DATA></COL>
      <COL><DATA>Type</DATA></COL>
      <COL><DATA>active</DATA></COL>
      <xsl:for-each select="$fields">
        <COL><DATA><xsl:value-of select="." /></DATA></COL>
      </xsl:for-each>
    </HEADER>
    <xsl:apply-templates select="patient/diagnoses/*" />
  </TABLE>
</xsl:template>

<xsl:template match="diagnoses/*">
  <ROW>
    <COL><DATA><xsl:value-of select="../../link_id" /></DATA></COL>
    <COL><DATA><xsl:value-of select="local-name()" /></DATA></COL>
    <xsl:variable name="this"    select="." as="element()" />
    <xsl:variable name="disease" select="local-name()" />
    <xsl:for-each select="for $f in $fields return concat($disease,'_',$f)">
      <COL><DATA>
        <xsl:value-of select="$this/*[local-name()=current()]" />
      </DATA></COL>
    </xsl:for-each>
  </ROW>
</xsl:template>

</xsl:stylesheet>

答案 2 :(得分:0)

如果诊断值始终以已知顺序给出,则可以执行以下操作:

Set mySet = new TreeSet<Object>(new MyComparator());
mySet.add(new Object());
mySet.add(new Object());//same class already in set
mySet.add("wtf");

//mySet.size() is now 2 - the second "new Object()" was not inserted due to the comparator check

虽然我怀疑您想将日期转换为您自己的日期格式,但也许:

<xsl:template match="/data">
    <xsl:for-each select="patient/diagnoses/*">
        <ROW MODID="" RECORDID="">
            <COL><DATA><xsl:value-of select="../../link_id"/></DATA></COL>
            <COL><DATA><xsl:value-of select="name()"/></DATA></COL>
            <xsl:for-each select="*">
                <COL><DATA><xsl:value-of select="."/></DATA></COL>
            </xsl:for-each>
        </ROW>
    </xsl:for-each>
</xsl:template>

假设您需要<xsl:template match="/data"> <xsl:for-each select="patient/diagnoses/*"> <ROW MODID="" RECORDID=""> <COL><DATA><xsl:value-of select="../../link_id"/></DATA></COL> <COL><DATA><xsl:value-of select="name()"/></DATA></COL> <COL><DATA><xsl:value-of select="*[1]"/></DATA></COL> <COL><DATA><xsl:value-of select="*[2]"/></DATA></COL> <xsl:variable name="date" select="*[3]"/> <COL><DATA> <xsl:value-of select="substring($date, 1, 2)"/> <xsl:text>/</xsl:text> <xsl:value-of select="substring($date, 3, 2)"/> <xsl:text>/</xsl:text> <xsl:value-of select="substring($date, 5, 4)"/> </DATA></COL> </ROW> </xsl:for-each> </xsl:template>