【问题标题】:How to use XML attributes, instead of element tags to build a database with XSLT如何使用 XML 属性而不是元素标签来使用 XSLT 构建数据库
【发布时间】:2017-12-21 11:54:55
【问题描述】:

简介:

  • 我有一个已转换为 XML 文件的本机二进制文件。
  • 我的任务是创建一种方法来尽可能简单地访问这些 XML 文件中的数据(由于我缺乏编程知识!)
  • 我想,我将创建一个带有 IFRAME 的 HTML 网页,该网页链接到保存这些 XML 文件的存储库,然后可以从我们的内部 Intranet 链接到该网页。
  • 这将打开所需的 XML 文件 - 在浏览器中 - 格式类似于显示所有相关信息的电子表格。
  • 因此,公司内可以通过他们提供的设备访问我们网络的每个人都可以在需要时访问这些文件
  • 我在岗位上当学徒才4个月左右,这个任务已经飘了好几年了,但是大家都有大鱼要炒!所以这是一个很好的学习曲线(尽管很陡),我可以在团队中找到自己的位置:)

更新 #2

抱歉删除我之前写的所有内容。对于原始代码sn-ps,请参阅Daniel's answer。我觉得我现在能够表达我的问题了,现在我有了更好的理解并取得了一些进展......

  • 我使用的是 XLS 版本 1.0。明天我可能会用 2.0 进行测试,但我知道 1.0 有效,我只使用过 1.0。 (由于我在我的个人笔记本电脑上进行热点,我花了一段时间将 Daniel 的逐行帮助复制到我的工作计算机上,出于安全原因,这些计算机都处于脱机状态。

  • 我注意到我的原始代码应该更好地反映我的源代码,因此请参阅下面的示例数据集 XML。我遇到的困难是,由于显而易见的原因,我不能只复制原始数据集。它也是一个大文件(并且有数千个相同类型的文件,所有 ~900 行和 ~80,000 个字符) - [我发现这对我的开发很有帮助,但必须重新输入所有内容并更改元素名称等!]

`

  <unit typeNo="2" typeName="Digital" name="Digital Board" index="1" addr="10">
    <analogue>
    </analogue>
    <digital>
      <PNT point="1" description="something" number="8000" />
      <PNT point="2" description="something else" number="9000" />
    </digital>
    <control>
    </control>
  </unit>

  <unit typeNo="3" typeName="Control" name="Control Board" index="6" addr="15">
    <analogue>
    </analogue>
    <digital>
    </digital>
    <control>
      <PNT point="1" description="something" number="5000" />
      <PNT point="2" description="something else" number="5050" />
    </control>
  </unit>
</devices>`

@Daniel-Haley 到目前为止,非常好,感谢您的意见!我已经使用了您的方法所有属性都是已知的,因为我知道它们!我知道所有内容都显示在浏览器中,显示为文本,而不是代码,所以这是朝着正确方向迈出的一大步。

现在,您会注意到与我的 OP 不同的是,每个单元都有模拟、数字或控制板。根据其在字段中的功能为它们分配一个值。我们的工程师将使用这些数据来更新基于 XML 文件数据库的配置(因此,这是每个单元当前配置的数据库),用于参考、备份和编程更换单元。

  • 在搜索了我的原始数据后,我注意到每个&lt;UNIT&gt; 下都有&lt;Digital&gt; , &lt;Analogue&gt;, &lt;Control&gt; 子元素。这些仅在单位类型匹配时才会填充。我认为由于使用大量数据

  • ,存在很多一致性
  • 当 XML 文件与样式表一起显示时,多余的字段是可见的,增加了体积并使输出变得非常繁忙。我现在想省略任何不相关的空白字段

  • 我还想创建一个键,我可以在其中分配 &lt;typeNo&gt; 值,然后可以反映在输出中,因此源文件例如 &lt;typeNo="1"&gt; 可能相当于数字,并且在表格标题中, XLS 可以查找 Key,并根据该值显示类型

  • &lt;unit&gt;标签中还有多余的属性不需要在输出中显示。

感谢大家到目前为止的帮助,也提前感谢大家!

为我漫无边际的长描述道歉。我希望我已经明确了我的目标是什么,并且对此知之甚少,这让我很难清楚地表达我想要做什么,并且无法复制所有源代码,我慢慢了解这方面的知识,请耐心等待:D

【问题讨论】:

  • 请分享所需的输出结构,以便了解哪些属性将用于提取值。
  • 您好 Aniket,感谢您的反馈,我添加了所需输出的快速屏幕截图,您可以看到它基本上是一个表格,其中映射了信息。我还在代码中添加了“Point”,因为这实际上很重要(很抱歉之前错过了它!)
  • 您正在使用/能够使用哪个版本的 XSLT?
  • 您好,抱歉回复晚了!已经休息了几个星期,我使用的是 1.0 版,现在刚回到办公室,将尝试完成所有工作并赶上进度!

标签: xml xslt xpath


【解决方案1】:

您似乎想要为每个 ANG 一个新行,因此您必须更改此设置:

<xsl:for-each select="devices/peripheral/analog">
  <tr>
    <td><xsl:value-of select="ANG/@point"></td>
    <td><xsl:value-of select="ANG/@something"></td>
    <td><xsl:value-of select="ANG/@anything"></td>
  </tr>
</xsl:for-each>

到这里:

<xsl:for-each select="devices/peripheral/analog/ANG">
  <tr>
    <td><xsl:value-of select="@point"></td>
    <td><xsl:value-of select="@something"></td>
    <td><xsl:value-of select="@anything"></td>
  </tr>
</xsl:for-each>

这是假设您知道所有属性。

如果所有属性都已知,这就是我将如何做一个完整的例子......

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
  <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>

  <xsl:template match="/*">
    <html>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="peripheral|internal|external">
    <div>
      <h1><xsl:value-of select="translate(local-name(),$lower,$upper)"/></h1>
      <xsl:apply-templates select="unit/*"/>      
    </div>
  </xsl:template>

  <xsl:template match="unit/*">    
    <table>
      <thead>
        <tr>
          <th colspan="3">
            <xsl:value-of select="
              concat(
              translate(substring(local-name(),1,1),$lower,$upper),
              substring(local-name(),2),
              ' Board ')"/>
            <xsl:number from="peripheral|internal|external" level="any"/>
          </th>
        </tr>
        <tr>
          <th>point</th>
          <th>something</th>
          <th>anything</th>
        </tr>
      </thead>
      <tbody>
        <xsl:for-each select="ANG">
          <tr>
            <td><xsl:value-of select="@point"/></td>
            <td><xsl:value-of select="@something"/></td>
            <td><xsl:value-of select="@anything"/></td>
          </tr>
        </xsl:for-each>
      </tbody>
    </table>
  </xsl:template>

</xsl:stylesheet>

如果属性未知,我将使用Muenchian Grouping/xsl:key 为每个analog|digital 的ANG 子级创建一个唯一的属性名称列表。

我将使用该列表来确定列数(对于 colspan),填充标题列,并为每一行填充列(每个 ANG 一个新行)。

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="kAttrs" match="ANG/@*" 
    use="concat(generate-id(ancestor::digital|ancestor::analog),'|',local-name())"/>

  <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
  <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>

  <xsl:template match="/*">
    <html>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="peripheral|internal|external">
    <div>
      <h1><xsl:value-of select="translate(local-name(),$lower,$upper)"/></h1>
      <xsl:apply-templates select="unit/*"/>      
    </div>
  </xsl:template>

  <xsl:template match="unit/*">
    <xsl:variable name="id" select="generate-id()"/>
    <!--
      String containing a unique list of attributes from ANG children
      (separated by |). 
      Used to guarantee that each row has the right value in each column.
      Example value: point|something|anything|
    -->
    <xsl:variable name="attrs">
      <xsl:for-each select="ANG/@*[count(.|
        key('kAttrs',concat($id,'|',local-name()))[1]
        )=1]">
        <xsl:value-of select="concat(local-name(),'|')"/>
      </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="colspan" 
      select="string-length($attrs) - string-length(translate($attrs,'|',''))"/>
    <xsl:message><xsl:value-of select="local-name()"/> $attrs: <xsl:value-of select="$attrs"/></xsl:message>
    <table>
      <thead>
        <tr>
          <th colspan="{$colspan}">
            <xsl:value-of select="
              concat(
              translate(substring(local-name(),1,1),$lower,$upper),
              substring(local-name(),2),
              ' Board ')"/>
            <xsl:number from="peripheral|internal|external" level="any"/>
          </th>
        </tr>
        <tr>
          <!--Recursive template call to get the attribute names.-->
          <xsl:call-template name="getVal">
            <xsl:with-param name="attrs" select="$attrs"/>
            <xsl:with-param name="type" select="'head'"/>
          </xsl:call-template>
        </tr>
      </thead>
      <tbody>
        <!--Each ANG is a new row.-->
        <xsl:for-each select="ANG">
          <tr>
            <!--Recursive template call to get the attribute values.-->
            <xsl:call-template name="getVal">
              <xsl:with-param name="attrs" select="$attrs"/>
              <xsl:with-param name="type" select="'body'"/>
            </xsl:call-template>
          </tr>
        </xsl:for-each>
      </tbody>
    </table>
  </xsl:template>

  <xsl:template name="getVal">
    <xsl:param name="attrs"/>
    <xsl:param name="type"/>
    <xsl:variable name="currentAttr" select="substring-before($attrs,'|')"/>
    <xsl:variable name="remainingAttrs" select="substring-after($attrs,'|')"/>
    <xsl:choose>
      <xsl:when test="$type = 'head'">
        <th><xsl:value-of select="$currentAttr"/></th>
      </xsl:when>
      <xsl:when test="$type = 'body'">
        <td><xsl:value-of select="@*[local-name()=$currentAttr]"/></td>        
      </xsl:when>
    </xsl:choose>
    <!--This is what makes the template recursive.-->
    <xsl:if test="$remainingAttrs">
      <xsl:call-template name="getVal">
        <xsl:with-param name="attrs" select="$remainingAttrs"/>
        <xsl:with-param name="type" select="$type"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

如果可以使用 XSLT 2.0,那就简单多了……

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="/*">
    <html>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="peripheral|internal|external">
    <div>
      <h1><xsl:value-of select="upper-case(local-name())"/></h1>
      <xsl:apply-templates select="unit/*"/>      
    </div>
  </xsl:template>

  <xsl:template match="unit/*">
    <!--Sequence containing a unique list of attributes from ANG children. 
      Used to guarantee that each row has the right value in each column.-->
    <xsl:variable name="attrs" select="distinct-values(ANG/@*/local-name())"/>
    <table>
      <thead>
        <tr>
          <th colspan="{count($attrs)}">
            <xsl:value-of select="
              concat(
              upper-case(substring(local-name(),1,1)),
              substring(local-name(),2),
              ' Board ')"/>
            <xsl:number from="peripheral|internal|external" level="any"/>
          </th>
        </tr>
        <tr>
          <xsl:for-each select="$attrs">
            <th>
              <xsl:value-of select="."/>
            </th>
          </xsl:for-each>
        </tr>
      </thead>
      <tbody>
        <xsl:for-each select="ANG">
          <xsl:variable name="current" select="current()"/>
          <tr>
            <xsl:for-each select="$attrs">
              <td>
                <xsl:value-of select="$current/@*[local-name()=current()]"/>
              </td>
            </xsl:for-each>
          </tr>
        </xsl:for-each>
      </tbody>
    </table>
  </xsl:template>

</xsl:stylesheet>

上述所有三个样式表,使用这个输入:

<devices>
    <peripheral>
        <unit type="1" name="foo" version="1" index="01A" address="02B" parentType="1" parentIndex="9">
            <analog>
                <ANG point="1" something="foo" anything="bar" />
                <ANG point="2" something="oof" anything="rab" />
            </analog>
            <digital>
                <ANG point="1" something="foo" anything="bar" />
                <ANG point="2" something="oof" anything="rab" />
            </digital>
        </unit>
    </peripheral>
    <internal>
        <unit type="2" name="foobar" version="2" index="90Z" address="50X" parentType="7" parentIndex="5">
            <analog>
                <ANG point="1" something="foo" anything="bar" />
                <ANG point="2" something="oof" anything="rab" />
            </analog>
            <digital>
                <ANG point="1" something="foo" anything="bar" />
                <ANG point="2" something="oof" anything="rab" />
            </digital>
        </unit>
    </internal>   
</devices>

将产生以下输出...

<html>
   <body>
      <div>
         <h1>PERIPHERAL</h1>
         <table>
            <thead>
               <tr>
                  <th colspan="3">Analog Board 1</th>
               </tr>
               <tr>
                  <th>point</th>
                  <th>something</th>
                  <th>anything</th>
               </tr>
            </thead>
            <tbody>
               <tr>
                  <td>1</td>
                  <td>foo</td>
                  <td>bar</td>
               </tr>
               <tr>
                  <td>2</td>
                  <td>oof</td>
                  <td>rab</td>
               </tr>
            </tbody>
         </table>
         <table>
            <thead>
               <tr>
                  <th colspan="3">Digital Board 1</th>
               </tr>
               <tr>
                  <th>point</th>
                  <th>something</th>
                  <th>anything</th>
               </tr>
            </thead>
            <tbody>
               <tr>
                  <td>1</td>
                  <td>foo</td>
                  <td>bar</td>
               </tr>
               <tr>
                  <td>2</td>
                  <td>oof</td>
                  <td>rab</td>
               </tr>
            </tbody>
         </table>
      </div>
      <div>
         <h1>INTERNAL</h1>
         <table>
            <thead>
               <tr>
                  <th colspan="3">Analog Board 1</th>
               </tr>
               <tr>
                  <th>point</th>
                  <th>something</th>
                  <th>anything</th>
               </tr>
            </thead>
            <tbody>
               <tr>
                  <td>1</td>
                  <td>foo</td>
                  <td>bar</td>
               </tr>
               <tr>
                  <td>2</td>
                  <td>oof</td>
                  <td>rab</td>
               </tr>
            </tbody>
         </table>
         <table>
            <thead>
               <tr>
                  <th colspan="3">Digital Board 1</th>
               </tr>
               <tr>
                  <th>point</th>
                  <th>something</th>
                  <th>anything</th>
               </tr>
            </thead>
            <tbody>
               <tr>
                  <td>1</td>
                  <td>foo</td>
                  <td>bar</td>
               </tr>
               <tr>
                  <td>2</td>
                  <td>oof</td>
                  <td>rab</td>
               </tr>
            </tbody>
         </table>
      </div>
   </body>
</html>

要查看后两个样式表如何处理不同的输入,请开始向 ANG 元素添加新属性。

【讨论】:

  • 新年快乐!感谢您做出如此全面的回复 - 我会仔细阅读并让您知道我的答案!干杯丹尼尔,让你保持更新! :)
  • 嘿@daniel-haley,首先,我要再次感谢您的意见。刚刚完成了您告诉我的所有内容,复制了您的代码并进行了一些编辑以适合我的源代码关于这种语言,我肯定离我的最终结果更近了很多步!我现在还有更多问题(我想我工作越多,我最终会遇到的问题越多!) - 我会将 OP 更新到我现在的位置!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-13
  • 1970-01-01
  • 1970-01-01
  • 2018-09-22
  • 1970-01-01
  • 2020-06-19
相关资源
最近更新 更多