.net / xslt:<xsl:include>具有相对路径会导致错误

时间:2017-07-04 09:18:36

标签: c# .net xml xslt

我的C#程序必须生成不同版本的不同PDF文档。基本页眉和页脚是相同的。所以我想将它们放在一个单独的xsl中。

这里是我喜欢的文件夹结构:

/common/headerFooter.xsl
/docVersion1/doc1.xsl
/docVersion1/doc2.xsl
...
/docVersion2/doc1a.xsl
/docVersion2/doc2a.xsl
...

所以实际上包含在例如doc1.xsl应该像:

<xsl:include href="../common/headerFooter.xsl"/>

但是我收到以下错误:

[Fatal Error] :1:1 Content is not allowed in prolog.
System-ID unknown; Zeilennummer1; Spaltennummer1; org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.

好的 - 第一个想法是关于BOM等,但没有。文件完全没问题(!)。

所以我试了一下:

/docVersion1/headerFooter.xsl
/docVersion1/doc1.xsl
/docVersion1/doc2.xsl
...
/docVersion2/headerFooter.xsl
/docVersion2/doc1a.xsl
/docVersion2/doc2a.xsl
...

在doc1.xsl(等)中:

<xsl:include href="headerFooter.xsl"/>

这个功能......(!?!?)

问题出在哪里?

第一次尝试的相对路径是正确的。 Visual Studio也告诉它。我认为在每个docVersion文件夹中放置headerFooter.xsl的副本会很奇怪。

最近的代码:

// xmlToPdfInfo holds all important information about the stylesheets.
// xmlToPdfInfo.XslPath : path to the doc_.xsl-files
java.io.File xsltfile = new java.io.File(xmlToPdfInfo.XslPath);

StreamSource streamSource = new StreamSource(xsltfile.getAbsoluteFile());

// ERROR LINE: 
Transformer transformer = factory.newTransformer(streamSource);
// It seems there is already an analyse for all includes and it fails to
// get the relativ path correctly.
// No chance to put additional information e.g. about the path using 
// parameters.


// Set the value of a <param> in the stylesheet);
if (xmlToPdfInfo.Arguments != null)
{
     IList keys = xmlToPdfInfo.Arguments.GetKeyList();
     foreach (var key in keys)
     {
          Object value = xmlToPdfInfo.Arguments[key];
          try
          {
              transformer.setParameter(key.ToString(), value);
          }
          catch (Exception spe)
          {
                Console.WriteLine(spe.Message);
          }                        
     }
 }

提醒:将headerFooter.xsl放在docVersion文件夹中都可以。退步(.../)似乎存在问题。

1 个答案:

答案 0 :(得分:0)

最后我自己得到了答案:

重点是写自己的URIResolver!这是我的版本/方式:

public void createPDF(...) {
   ...
   xmlToPdfInfo.Resolver = new XmlResourceResolver(...);
   xmlToPdfInfo.XslPath = getPathToMyStyleSheet();
   xmlToPdfInfo.IncludedDocsList = GetIncludedDocsList();

   Utils.XmlToPdf(xmlToPdfInfo, outputStream);
}

protected SortedList GetIncludedDocsList()
{
    SortedList sortedList = new SortedList();
    sortedList.Add("headerFooter.xsl", BasePath() + ".common.headerFooter.xsl");
    ...
    return sortedList;
}
public class Utils
{
    private static XmlResourceResolver resolver;
    private static SortedList includedDocsList;

    /// <summary>Gets a PDF as stream from an xml-file, rendered by an xsl stylesheet</summary>
    /// <param name="xmlToPdfInfo">Contains all input information</param>
    /// <param name="outputStream">resulting PDF as a stream</param>
    public static void XmlToPdf(XmlToPdfInfo xmlToPdfInfo, OutputStream outputStream)
    {
        resolver = xmlToPdfInfo.Resolver;
        includedDocsList = xmlToPdfInfo.IncludedDocsList;
        ...
    }

    public class VgUriResolver : URIResolver
    {
        /// <summary>Gets the embedded file for the UriResolver</summary>
        /// <param name="href">relative path to the file (the path that has been put in <xsl:include>)</param>
        /// <param name="baseUri">base path</param>
        /// <returns>The embedded source </returns>
        public Source resolve(String href, String baseUri)
        {
            if (includedDocsList != null)
            {
                IList keys = includedDocsList.GetKeyList();
                String hrefFilename = Path.GetFileName(href);

                foreach (var key in keys)
                {
                    String pfad = (string) includedDocsList[key];
                    String filename = (String) key;

                    try
                    {
                        // "hard-match": if by chance we can get the file
                        // by the real resource path.
                        if (pfad.Equals(href))
                        {
                            byte[] bArr = ReadToEnd(resolver.GetManifestResourceStream(pfad));
                            return new StreamSource(new ByteArrayInputStream(bArr));
                        }

                        // "soft-match": looks for the filenames without any path. 
                        // Attention: works only fine if you **don't** have to include files
                        // with same name (different path)                          
                        if (filename.Equals(hrefFilename))
                        {
                            byte[] bArr = ReadToEnd(resolver.GetManifestResourceStream(pfad));
                            return new StreamSource(new ByteArrayInputStream(bArr));
                        }
                    }
                    catch (Exception)
                    {
                        try
                        {
                            return readFromFileStream(href);
                        }
                        catch (Exception)
                        {
                            return readFromFileStream(hrefFilename);
                        }
                    }
                }                 
            }
            return new StreamSource(new StringReader(href));
        }
    }

}

事实上,您所写的内容更多是使用自己的URIResolver查找真实文件的键/占位符。

嗯...