使用MemoryStream的XSLT编译错误

时间:2012-10-04 17:58:16

标签: c# asp.net xslt

我正在动态创建一个xslt和xml文件来显示我的问卷&保存我的问题选项(用于下拉列表)。现在我想使用流而不是在实际文件上写。所以我就是这样做的:

XmlReader xslt_reader;
XmlReader xml_reader;

PageLoad(){
  Fn_CreateXSL();
  Fn_CreateXML();
  LoadQuestionnaire();
}

Fn_CreateXSL(){
   xslt_stream = new MemoryStream();

   XmlTextWriter objXSLTWriter = new XmlTextWriter(xslt_stream, Encoding.UTF8);

   objXSLTWriter.Formatting = Formatting.Indented;
   objXSLTWriter.WriteStartDocument();
    ..........
   objXSLTWriter.WriteEndDocument();
   xslt_stream.Seek(0, SeekOrigin.Begin);
   xslt_reader = XmlReader.Create(xslt_stream);
}

Fn_CreateXML(){
   xmlt_stream = new MemoryStream();

   XmlTextWriter objXMLTWriter = new XmlTextWriter(xmlt_stream, Encoding.UTF8);

   objXMLTWriter.Formatting = Formatting.Indented;
   objXMLTWriter.WriteStartDocument();
    ..........
   objXMLTWriter.WriteEndDocument();
   xmlt_stream.Seek(0, SeekOrigin.Begin);
   xmlt_reader = XmlReader.Create(xmlt_stream);
}

LoadQuestionnaire(){
  XslCompiledTransform var_xsl_trans = new XslCompiledTransform();

  // also tried var_xsl_trans.Load(xslt_reader, null, new XmlUrlResolver());
  var_xsl_trans.Load(xslt_reader); // XSLT Compile Error occurs

  StringWriter sw = new StringWriter();

  var_xsl_trans.Transform(xml_reader, null, sw);
}

但是当我尝试加载它时,我得到“XSLT编译错误。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

这是适合我的方法。

我将使用Microsoft引入的XElement和.NET 3.5中的LINQ,而不是使用XmlWriter-> MemoryStream-> XmlReader。 Load类的TransformXslCompiledTransform方法都有重载,以IXPathNavigable作为参数传递XML。此接口只有一个方法XPathNavigator CreateNavigator()XElement具有此方法。我不知道微软为什么没有声明XElement实现了这个接口。可能他们忘了:-)。因此,为了弥补这种疏忽,我实施了6行转换器类XNavigable。你会在下面看到它。这是OP的Page_Load()的类比:

public void Generate()
{
    XElement inputXml = CreateInputXml();
    XElement transformXslt = CreateTransformXslt();

    XslCompiledTransform transform = new XslCompiledTransform();
    transform.Load(new XNavigable(transformXslt), null, null);

    MemoryStream outputStream = new MemoryStream(); 
    transform.Transform(new XNavigable(inputXml), null, outputStream);

    byte[] outputBytes = outputStream.ToArray();
    string outputString = Encoding.UTF8.GetString(outputBytes);
}

我使用MemoryStream而不是StringWriter,因为StringWriter总是为我创建UTF-16编码的XML作为输出。我认为StringFriter强加了UTF-16,因为底层字符串每个字符有16位。使用MemoryStream,我们可以控制编码。

为了完整性,这里是我的CreateInputXmlCreateTransformXslt方法的代码。它们只是一个例子。当然,XElement.Add()方法可用于生成内容。我只是使用了其中列出的所有节点的构造函数来快速硬编码。

    private static XElement CreateTransformXslt()
    {
        //XSL to substitute <placeholder/> with <realElement/> and copy everything else.
        //<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
        //  <output indent="yes" />
        //  <template match="@* | node()">
        //    <copy>
        //      <apply-templates select="@* | node()" />
        //    </copy>
        //  </template>
        //  <template match="placeholder">
        //    <realElement xmlns="" />
        //  </template>
        //</stylesheet>
        XNamespace xsl = "http://www.w3.org/1999/XSL/Transform";
        XNamespace empty = "";
        XElement transformXslt = new XElement(xsl + "stylesheet", new XAttribute("version", "1.0"),
            new XElement(xsl + "output", new XAttribute("indent", "yes")),
            //new XElement(xsl + "strip-space", new XAttribute("elements", "*")),
            new XElement(xsl + "template", new XAttribute("match", "@* | node()"),
                new XElement(xsl + "copy",
                    new XElement(xsl + "apply-templates", new XAttribute("select", "@* | node()"))
                )
            ),
            new XElement(xsl + "template", new XAttribute("match", "placeholder"),
                new XElement("realElement")
            )
        );
        return transformXslt;
    }

    private static XElement CreateInputXml()
    {
        XElement origXml = new XElement(new XElement("Root",
                                            new XElement("Child1", "data1"),
                                            new XElement("placeholder"),
                                            new XElement("Child2", "data2")));
        return origXml;
    }

上面代码中的一个问题是我们不应该在XSL中使用xsl:strip-space elements="*"。上面评论过。这是因为当输入xml作为XslCompiledTransform传递时,IXPathNavigable在删除空间方面存在问题。在错误消息中,如果我需要剥离,则建议使用XmlReader。我不认为剥离空间对于生成网页很重要。

最后,这里是开头提到的转换器类:

class XNavigable : IXPathNavigable
{
    XElement _xElement;
    public XNavigable(XElement xElement)
    {
        _xElement = xElement;
    }

    public XPathNavigator  CreateNavigator()
    {
            return _xElement.CreateNavigator();
    }
}

答案 1 :(得分:0)

正如@kevin在评论中提到的,以下问题解决了我的问题,但我不太清楚为什么它有效(有一些想法,但不确定)

  

在xmlt_stream.Seek(0之前添加objXMLTWriter.Flush()   SeekOrigin.Begin);

相关问题