【问题标题】:XSLT to bind data with view in ms xslt works, but errors in saxonXSLT 可以在 ms xslt 中将数据与视图绑定,但 saxon 中的错误
【发布时间】:2021-02-27 19:42:22
【问题描述】:

这是我正在尝试做的事情的一个简单示例。我可以在 MS XSLT 中获得一些工作(使用 Visual Studio)。

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

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

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

如果我们获取一些数据。

<?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>

即数据行和视图列的叉积以构建表。 其他一切都应该复制,视图可能包含各种 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)

【问题讨论】:

  • 显示运行转换的 C# 代码
  • 这是一个稍微复杂的设置,我必须创建一个定制函数来展示基础知识,给我 10 分钟左右
  • 是“view.xml”调用了“apply-templates”吗?这是我第一次使用document(),我也不经常应用模板。

标签: xslt xslt-1.0 saxon


【解决方案1】:

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

【讨论】:

  • tbh 我非常讨厌将全局变量设置为源文档的某个子集,然后使用 document() 翻转上下文...在 XSLT 中有更简洁的方法吗?跨度>
  • @MrDatKookerellaLtd,我试图用这个答案解释和解决撒克逊Xslt30Transformer的API使用问题,任何全局xsl:variablexsl:param试图选择初始匹配选择都会运行进入这个问题,无论您是否在某处使用document()。因此,为了使用全局参数,您需要在我对 API 的理解中使用 Transform 方法,或者自己构建输入 XdmNode 并设置 GlobalContextItem,这在模式感知 XSLT 和验证的情况下可能会变得复杂。
  • 基本上,如果您想复制 XSLT 1.0 处理器的行为方式,最简单的方法是使用 Transform 方法(这会将全局上下文项设置为初始 ApplyTemplates 应用到的节点)。正如您所说,最好避免使用绑定到源文档的全局变量(这使得将代码作为管道的一部分重用变得更加困难)。 XSLT 2.0+ 的做法是将 $customers 绑定到隧道参数;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-09
  • 2017-06-09
  • 1970-01-01
  • 2011-12-10
  • 2013-05-03
  • 2016-03-13
  • 1970-01-01
相关资源
最近更新 更多