XSLT 将数据与 ms xslt 中的视图绑定,但在 saxon 中出错

XSLT to bind data with view in ms xslt works, but errors in saxon

这是我正在尝试做的事情的一个简单示例。我可以在 MS XSLT 中使用一些东西(使用 visual studio)。

我们的想法是采用一个“视图”,它是一个 XML 定义布局的外观。

<?xml version="1.0" encoding="utf-8" ?>
<rows>
  <row>
    <column value="name"/>
    <column value="id"/>
  </row>
</rows>

XSLT 应该采用布局视图并将除每个“行”之外的所有内容复制到输出,它应该在输出中创建一行 table,并根据值解释列。

所以如果我们获取一些数据。

<?xml version="1.0" encoding="utf-8" ?>
<customers>
  <customer id="123" name="Mr Bloggs"/>
  <customer id="124" name="Mrs Smith"/>
</customers>

我们希望输出看起来像

<?xml version="1.0" encoding="utf-8" ?>
<rows>
  <row>
    <column value="Mrs Smith"/>
    <column value="124"/>
  </row>
  <row>
    <column value="Mr Bloggs"/>
    <column value="123"/>
  </row>
</rows>

即数据行和视图列的叉积构建一个 table。 其他一切都应该复制,视图可能包含各种 XSLT 不需要理解的东西,它应该只是盲目地复制它们。

所以我在 MS XSLT 中的原型是这样的(可行,但对我来说似乎很笨重)。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="customers" select="/customers/customer"/>

  <xsl:template match="/customers" priority="1">
    <xsl:apply-templates select="document('View.xml')"/>
  </xsl:template>

  <xsl:template match="row" priority="1">
    <xsl:variable name="this" select="."/>
    <xsl:for-each select="$customers">
      <xsl:apply-templates select="$this" mode="processRow">
        <xsl:with-param name="customer" select="."/>
      </xsl:apply-templates>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="column" mode="processRow">
    <xsl:param name="customer"/>
    <xsl:copy>
      <xsl:choose>
        <xsl:when test="@value = 'name'">
          <xsl:value-of select="$customer/@name"/>
        </xsl:when>
        <xsl:when test="@value = 'id'">
          <xsl:value-of select="$customer/@id"/>
        </xsl:when>
      </xsl:choose>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@* | node()" mode="processRow">
    <xsl:param name="customer"/>
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"  mode="processRow">
        <xsl:with-param name="customer" select="$customer"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

目的是在一个全局变量中捕获数据(即客户),然后打开并处理视图,使用有点像身份转换的东西,但拦截“行”以“注入”数据,每个项目 1 行,并截取列以根据当前处理的项目解释视图。其他一切都只是复制过来,同时通过即将处理的客户。

如果我 运行 通过 Saxon9ee-api (C#) 它会爆炸

Error on line 7 of file:///C:/Kookeralla/.....:
  XPDY0002: The context item for axis step root/customers is absent
  In template rule with match="row" on line 13 of file:///C:/Kookeralla/.....
     invoked by xsl:apply-templates at file:///C:/Kookeralla/....../#47
  In template rule with match="(((element() | text()) | comment()) | processing-instruction())" on line 45 of file:///C:/Kookeralla/......
     invoked by built-in template rule (text-only)
  In template rule with match="document-node()/element(Q{}customers)" on line 9 of file:///C:/Kookeralla/.....
     invoked by xsl:apply-templates at file:///C:/Kookeralla/..../Debug/#10
  In template rule with match="document-node()/element(Q{}customers)" on line 9 of file:///C:/Kookeralla/....../Debug/
     invoked by built-in template rule (text-only)

我不太了解(目前我想将其保留在 XSLT 1.0 中)。


编辑以包含 C# 代码,此代码适用于恒等变换

        var xslt = "BindModelToView.xslt";
        //var xslt = "identity.xslt";
        var data = "SimpleModel.xml";
        var baseDir = new Uri(new Uri("file://"), AppDomain.CurrentDomain.BaseDirectory);

        var xslt2 = new Uri(baseDir, xslt);
        Processor processor = new Processor(true);
        var compiler = processor.NewXsltCompiler();
        compiler.BaseUri = baseDir;
        compiler.GetUnderlyingCompilerInfo().setJustInTimeCompilation(false);
        var compile = compiler.Compile(xslt2);

        //Processor processor = new Processor(true);
        Serializer serializer = processor.NewSerializer();
        var output = new StringWriter();
        //serializer.SetOutputWriter(Console.Out);
        serializer.SetOutputWriter(output);
        var transformer = compile.Load30();
        // Transform the source XML and serialize the result document
        transformer.SchemaValidationMode = SchemaValidationMode.None;
        transformer.ApplyTemplates(File.OpenRead(data), serializer);
        return output.ToString();

此代码报告此

Error at char 1 in xsl:variable/@select on line 7 column 64 of BindModelToView.xslt:
  XPDY0002: The context item for axis step root/customers is absent
  In template rule with match="row" on line 13 of BindModelToView.xslt
     Focus
        Context item: /rows/row[1]
        Context position: 2
     Local variables
        $this = <row>
     invoked by xsl:apply-templates at file:///C:/Kookeralla/SaxonEEExample/ValidateXslt/bin/Debug/BindModelToView.xslt#47
  In template rule with match="( element() | text() | comment() | processing-instruction() )" on line 45 of BindModelToView.xslt
     invoked by built-in template rule (text-only)
  In template rule with match="document-node()/element(Q{}customers)" on line 9 of BindModelToView.xslt
     invoked by xsl:apply-templates at file:///C:/Kookeralla/SaxonEEExample/ValidateXslt/bin/Debug/BindModelToView.xslt#10
  In template rule with match="document-node()/element(Q{}customers)" on line 9 of BindModelToView.xslt
     invoked by built-in template rule (text-only)

我认为问题在于使用全局变量试图 select 输入文档,同时仅调用 ApplyTemplates 但不设置 GlobalContextItem。在这种情况下调用 transformer.Transform(File.OpenRead(data), serializer); 可能更容易。