.NET 2.0/VS2005
I am trying to use the XslCompiledTransform
class to perform a XSL Transformation. I have two XSL files, the first of which includes a reference to the other in the form of an <xsl:include>
statement :
我试图使用XslCompiledTransform类来执行XSL转换。我有两个XSL文件,第一个包含对
Main.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="Included.xsl" />
...
...
</xsl:stylesheet>
Now, If I could load the "Main.xsl" file itself as a URI, my transformation code would be as simple as :
现在,如果我可以将“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();
}
The problem is that I receive the contents of the Main.xsl file as a string and need to load the string as an XmlReader/IXpathNavigable
. This is a necessary restriction at this time. When I try to do the same using an XmlReader/XpathDocument
, it fails because the code looks for "Included.xsl" in the C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\
folder! Obviously, the XmlResolver
is not able to resolve the relative URL because it only receives a string as input XSL.
问题是我收到Main.xsl文件的内容为字符串,需要将字符串加载为XmlReader / IXpathNavigable。这是此时的必要限制。当我尝试使用XmlReader / XpathDocument执行相同操作时,它会失败,因为代码在C:\ Program Files \ Microsoft Visual Studio 8 \ Common7 \ IDE \文件夹中查找“Included.xsl”!显然,XmlResolver无法解析相对URL,因为它只接收一个字符串作为输入XSL。
My efforts in this direction look like:
我在这方面的努力看起来像:
// 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();
}
I have tried to use the ResolveUri
method of the XmlUrlResolver to obtain a Stream
referencing the XSL file to be included, but am confused as to how to use this Stream. IOW, how do I tell the XslCompiledTransform
object to use this stream in addition to the Main.xsl XmlReader:
我曾尝试使用XmlUrlResolver的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??
Any help is greatly appreciated. Sorry for the long post!
任何帮助是极大的赞赏。对不起,很长的帖子!
For your reference, the Exception StackTrace looks like this:
供您参考,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
4 个解决方案
#1
Use a custom XmlUrlResolver
使用自定义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);
}
}
And use it in load function of XslCompiledTransform,
并在XslCompiledTransform的加载函数中使用它,
resolver=new MyXmlUrlResolver();
xslt.Load(xR,null,resolver);
#2
I am probably missing the obvious but is there a reason you don't just change the URI of Included.xsl to be a true URL? This could either be done in the XSL doc if you have access or using string manipulation otherwise?
我可能错过了显而易见的但有没有理由你不只是将Included.xsl的URI更改为真正的URL?如果您有权访问或使用字符串操作,这可以在XSL文档中完成吗?
#3
As Gee's answer mentions, you want to use a custom XmlResolver
(of which XmlUrlResolver
is already derived), but if you also override the method GetEntity
you can resolve references in the primary XSLT document in fun and interesting ways. A deliberately simple example of how you could resolve the reference to Included.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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n <xsl:template name=\"Included\">\n\n </xsl:template>\n</xsl:stylesheet>";
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);
}
}
When you load the Main.xsl document you'd change the relevant code to the following:
加载Main.xsl文档时,您需要将相关代码更改为以下内容:
xslt.Load(xpDoc, new XsltSettings(true, true), new CustomXmlResolver());
The above example was based on info I picked-up in the MSDN article Resolving the Unknown: Building Custom XmlResolvers in the .NET Framework.
上面的示例基于我在MSDN文章“解决未知:在.NET Framework中构建自定义XmlResolvers”中提到的信息。
#4
I already have success with doing transformations using all in memory:
我已经成功使用内存中的所有内容进行转换:
Having a xslt containing the following includes:
拥有包含以下内容的xslt包括:
import href="Common.xslt" and import href="Xhtml.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 "";
}
}
Note that absolutely all content is as strings: styleSheet, xmlToParse and the content of the "Common" and "Xhtml" imports
请注意,绝对所有内容都是字符串:styleSheet,xmlToParse以及“Common”和“Xhtml”导入的内容
#1
Use a custom XmlUrlResolver
使用自定义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);
}
}
And use it in load function of XslCompiledTransform,
并在XslCompiledTransform的加载函数中使用它,
resolver=new MyXmlUrlResolver();
xslt.Load(xR,null,resolver);
#2
I am probably missing the obvious but is there a reason you don't just change the URI of Included.xsl to be a true URL? This could either be done in the XSL doc if you have access or using string manipulation otherwise?
我可能错过了显而易见的但有没有理由你不只是将Included.xsl的URI更改为真正的URL?如果您有权访问或使用字符串操作,这可以在XSL文档中完成吗?
#3
As Gee's answer mentions, you want to use a custom XmlResolver
(of which XmlUrlResolver
is already derived), but if you also override the method GetEntity
you can resolve references in the primary XSLT document in fun and interesting ways. A deliberately simple example of how you could resolve the reference to Included.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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n <xsl:template name=\"Included\">\n\n </xsl:template>\n</xsl:stylesheet>";
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);
}
}
When you load the Main.xsl document you'd change the relevant code to the following:
加载Main.xsl文档时,您需要将相关代码更改为以下内容:
xslt.Load(xpDoc, new XsltSettings(true, true), new CustomXmlResolver());
The above example was based on info I picked-up in the MSDN article Resolving the Unknown: Building Custom XmlResolvers in the .NET Framework.
上面的示例基于我在MSDN文章“解决未知:在.NET Framework中构建自定义XmlResolvers”中提到的信息。
#4
I already have success with doing transformations using all in memory:
我已经成功使用内存中的所有内容进行转换:
Having a xslt containing the following includes:
拥有包含以下内容的xslt包括:
import href="Common.xslt" and import href="Xhtml.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 "";
}
}
Note that absolutely all content is as strings: styleSheet, xmlToParse and the content of the "Common" and "Xhtml" imports
请注意,绝对所有内容都是字符串:styleSheet,xmlToParse以及“Common”和“Xhtml”导入的内容