如何解决XSL包含在从String加载XSL的转换中?

.NET 2.0 / VS2005

我试图使用XslCompiledTransform类来执行XSL转换。 我有两个XSL文件,第一个包含对语句forms的另一个引用:

Main.xsl:

   ... ...  

现在,如果我可以将“Main.xsl”文件本身作为URI加载,我的转换代码就像下面这样简单:

 // This is a function that works. For demo only. private string Transform(string xslFileURI) { XslCompiledTransform xslt = new XslCompiledTransform(); // This load works just fine, if I provide the path to "Main.xsl". // The xsl:include is automatically resolved. xslTransform.Load(xslFileURI); StringWriter sw = new StringWriter(); xslt.Transform(Server.MapPath("~/XML/input.xml"), null, sw); return sw.ToString(); } 

问题是我收到Main.xsl文件的内容为字符串,需要将字符串加载为XmlReader/IXpathNavigable这是此时的必要限制。 当我尝试使用XmlReader/XpathDocument执行相同操作时,它会失败,因为代码在C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\文件夹中查找“Included.xsl”! 显然, XmlResolver无法解析相对URL,因为它只接收一个字符串作为输入XSL。

我在这方面的努力看起来像:

 // This doesn't work! Halp! private string Transform(string xslContents) { XslCompiledTransform xslt = new XslCompiledTransform(); XmlUrlResolver resolver = new XmlUrlResolver(); resolver.Credentials = CredentialCache.DefaultCredentials; //METHOD 1: This method does not work. XmlReaderSettings settings = new XmlReaderSettings(); settings.XmlResolver = resolver; XmlReader xR = XmlReader.Create(new StringReader(xslContents), settings); xslt.Load(xR); // fails // METHOD 2: Does not work either. XPathDocument xpDoc = new XPathDocument(new StringReader(xslContents)); xslt.Load(xpDoc, new XsltSettings(true, true), resolver); //fails. StringWriter sw = new StringWriter(); xslt.Transform(Server.MapPath("~/XML/input.xml"), null, sw); return sw.ToString(); } 

我曾尝试使用ResolveUri方法来获取引用要包含的XSL文件的Stream ,但我对如何使用此Stream感到困惑。 IOW,除了Main.xsl XmlReader之外,我如何告诉XslCompiledTransform对象使用此流:

 Uri mainURI = new Uri(Request.PhysicalApplicationPath + "Main.xsl"); Uri uri = resolver.ResolveUri(mainURI, "Included.xsl"); // I can verify that the Included.xsl file loads in the Stream below. Stream s = resolver.GetEntity(uri, null, typeof(Stream)) as Stream; // How do I use this Stream in the function above?? 

任何帮助是极大的赞赏。 对不起,很长的post!

供您参考,Exception StackTrace如下所示:

 [FileNotFoundException: Could not find file 'C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Included.xsl'.] System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) +328 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) +1038 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize) +113 System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials) +78 System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn) +51 System.Xml.Xsl.Xslt.XsltLoader.CreateReader(Uri uri, XmlResolver xmlResolver) +22 System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(Uri uri, Boolean include) +33 System.Xml.Xsl.Xslt.XsltLoader.LoadInclude() +349 System.Xml.Xsl.Xslt.XsltLoader.LoadRealStylesheet() +704 System.Xml.Xsl.Xslt.XsltLoader.LoadDocument() +293 System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(XmlReader reader, Boolean include) +173 

使用自定义XmlUrlResolver

 class MyXmlUrlResolver : XmlUrlResolver { public override Uri ResolveUri(Uri baseUri, string relativeUri) { if (baseUri != null) return base.ResolveUri(baseUri, relativeUri); else return base.ResolveUri(new Uri("http://mypath/"), relativeUri); } } 

并在XslCompiledTransform的加载函数中使用它,

 resolver=new MyXmlUrlResolver(); xslt.Load(xR,null,resolver); 

我可能错过了显而易见的但有没有理由你不只是将Included.xsl的URI更改为真正的URL? 如果您有权访问或使用字符串操作,这可以在XSL文档中完成吗?

正如Gee的回答所提到的,你想要使用一个自定义的XmlResolver (其中已经派生了XmlUrlResolver ),但如果你也覆盖方法GetEntity你可以用有趣和有趣的方式解析主XSLT文档中的引用。 一个故意简单的示例,说明如何解析对Included.xsl的引用:

 public class CustomXmlResolver : XmlResolver { public CustomXmlResolver() { } public override ICredentials Credentials { set { } } public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) { MemoryStream entityStream = null; switch (absoluteUri.Scheme) { case "custom-scheme": string absoluteUriOriginalString = absoluteUri.OriginalString; string ctgXsltEntityName = absoluteUriOriginalString.Substring(absoluteUriOriginalString.IndexOf(":") + 1); string entityXslt = ""; // TODO: Replace the following with your own code to load data for referenced entities. switch (ctgXsltEntityName) { case "Included.xsl": entityXslt = "\n\n \n\n \n"; break; } UTF8Encoding utf8Encoding = new UTF8Encoding(); byte[] entityBytes = utf8Encoding.GetBytes(entityXslt); entityStream = new MemoryStream(entityBytes); break; } return entityStream; } public override Uri ResolveUri(Uri baseUri, string relativeUri) { // You might want to resolve all reference URIs using a custom scheme. if (baseUri != null) return base.ResolveUri(baseUri, relativeUri); else return new Uri("custom-scheme:" + relativeUri); } } 

加载Main.xsl文档时,您需要将相关代码更改为以下内容:

 xslt.Load(xpDoc, new XsltSettings(true, true), new CustomXmlResolver()); 

上面的示例基于我在MSDN文章“ 解决未知:在.NET Framework中构建自定义XmlResolvers”中提到的信息。

我已经成功使用内存中的所有内容进行转换:

拥有包含以下内容的xslt包括:

import href =“Common.xslt”并导入href =“Xhtml.xslt”

  private string Transform(string styleSheet, string xmlToParse) { XslCompiledTransform xslt = new XslCompiledTransform(); MemoryResourceResolver resolver = new MemoryResourceResolver(); XmlTextReader xR = new XmlTextReader(new StringReader(styleSheet)); xslt.Load(xR, null, resolver); StringWriter sw = new StringWriter(); using (var inputReader = new StringReader(xmlToParse)) { var input = new XmlTextReader(inputReader); xslt.Transform(input, null, sw); } return sw.ToString(); } public class MemoryResourceResolver : XmlResolver { public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) { if (absoluteUri.ToString().Contains("Common")) { return new MemoryStream(Encoding.UTF8.GetBytes("Xml with with common data")); } if (absoluteUri.ToString().Contains("Xhtml")) { return new MemoryStream(Encoding.UTF8.GetBytes("Xml with with xhtml data")); } return ""; } } 

请注意,绝对所有内容都是字符串:styleSheet,xmlToParse以及“Common”和“Xhtml”导入的内容