Я пытаюсь разрешить ссылки на функции документа в XslCompiledTransform, используя пользовательский XmlResolver, а не фактические файлы в файловой системе. Используя устаревший XslTransform, я могу заставить его работать нормально, но не с скомпилированным преобразованием (которое я хочу использовать по соображениям производительности).
Исходный XML-документ выглядит следующим образом:
<?xml version=""1.0"" encoding=""UTF-8""?>
<StackOverflow>
</StackOverflow>
XSLT выглядит так:
<xsl:stylesheet version=""1.0""
xmlns:xsl=""http://www.w3.org/1999/XSL/Transform""
xmlns:xs=""http://www.w3.org/2001/XMLSchema""
exclude-result-prefixes=""xs"">
<xsl:variable name=""author"" select=""document('data.xml')""/>
<xsl:template match=""/"">
<StackOverflow>
<Author>
<xsl:copy-of select=""$author/Person/*""/>
</Author>
</StackOverflow>
</xsl:template>
</xsl:stylesheet>
Ожидаемый результат:
<?xml version="1.0" encoding="utf-16"?>
<StackOverflow>
<Author>
<FirstName>Aaron</FirstName>
<LastName>Janes</LastName>
</Author>
</StackOverflow>
Часть, которую я хочу использовать пользовательский XmlResolver for, - это "документ (" data.xml "), который я не хочу загружать из файла на диске, но для краткости и простоты в этом сообщении, из статического словаря в пределах resolver, вот так:
namespace transform
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
public class XmlStaticResolver : XmlResolver
{
private const string PersonXml =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<Person>
<FirstName>Aaron</FirstName>
<LastName>Janes</LastName>
</Person>";
private readonly IDictionary<string, string> _input;
public XmlStaticResolver()
{
_input = new Dictionary<string, string>
{
{ "data.xml", PersonXml }
};
}
public override object GetEntity(Uri absoluteUri,
string role,
Type ofObjectToReturn)
{
var host = absoluteUri.Host;
var xml = _input[host];
var bytes = Encoding.UTF8.GetBytes(xml);
var result = new MemoryStream(bytes);
return result;
}
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
return new Uri("static://" + relativeUri);
}
}
}
Резольвер принимает относительный URI ("data.xml") и добавляет поддельный протокол, чтобы метод GetEntity рассматривал "data.xml" как хост URL-адреса. Затем значение просматривается в статическом словаре и возвращается как поток памяти для используемого преобразования.
Когда я использую этот метод с использованием устаревшего XmlTransform, он работает точно так, как ожидалось, например, вызывая метод Transform с вышеупомянутым XSLT, и XML получает ожидаемый результат:
namespace transform
{
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
public class ObsoleteTransform
{
public string Transform(string xslt, string xml)
{
var resolver = new XmlStaticResolver();
var transform = new XslTransform { XmlResolver = resolver };
using (var transformReader = CreateXmlReader(new StringReader(xslt), resolver))
{
transform.Load(transformReader);
}
using (var writer = new StringWriter())
{
using (var xwriter = CreateXmlWriter(writer))
{
using (var inputReader = new StringReader(xml))
{
var input = new XPathDocument(inputReader);
transform.Transform(input,
new XsltArgumentList(),
xwriter);
}
}
return writer.ToString();
}
}
private static XmlReader CreateXmlReader(TextReader reader,
XmlResolver resolver)
{
var settings = new XmlReaderSettings
{
XmlResolver = resolver
};
var xreader = XmlReader.Create(reader, settings, (string)null);
return xreader;
}
private static XmlWriter CreateXmlWriter(TextWriter writer)
{
var settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n"
};
var xwriter = XmlWriter.Create(writer, settings);
return xwriter;
}
}
}
Однако, когда я пытаюсь использовать XslCompiledTransform, я получаю исключение:
System.Xml.Xsl.XslTransformException:
An error occurred while loading document 'data.xml'.
Код, который я использую, чтобы попытаться преобразовать с помощью XslCompiledTransform:
namespace transform
{
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
public class CompiledTransform
{
public string Transform(string xslt, string xml)
{
var resolver = new XmlStaticResolver();
var xsltSettings = new XsltSettings(true, true);
var transform = new XslCompiledTransform();
using (var transformReader = CreateXmlReader(new StringReader(xslt), resolver))
{
transform.Load(transformReader, xsltSettings, resolver);
}
using (var writer = new StringWriter())
{
using (var xwriter = CreateXmlWriter(writer))
{
using (var inputReader = new StringReader(xml))
{
var input = new XPathDocument(inputReader);
transform.Transform(input,
new XsltArgumentList(),
xwriter);
}
}
return writer.ToString();
}
}
private static XmlReader CreateXmlReader(TextReader reader,
XmlResolver resolver)
{
var settings = new XmlReaderSettings
{
XmlResolver = resolver
};
var xreader = XmlReader.Create(reader, settings, (string)null);
return xreader;
}
private static XmlWriter CreateXmlWriter(TextWriter writer)
{
var settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n"
};
var xwriter = XmlWriter.Create(writer, settings);
return xwriter;
}
}
}
Мне кажется, что, используя скомпилированное преобразование, резольвер не вызывается. Кто-нибудь знает, почему?
Вы передаете свой XmlStaticResolver resolver
как для XmlReader.Create()
и для XslCompiledTransform.Load()
. Вы возвращаете свой PersonXml
/data.xml
, то есть XML, в обоих случаях. Но XslCompiledTransform.Load()
вызывает XmlStaticResolver.ResolveUri()
с Uris для разрешения таблицы стилей, т.е. путей к файлам .xslt
. Во всяком случае, я не вижу, что вы хотите передать свой XmlStaticResolver resolver
в CreateXmlReader(new StringReader(xslt), resolver))
который читает XSL; Я не знаю, как это работает в старом коде....
Я думаю, вы обнаружите, что ваша ошибка System.Xml.Xsl.XslTransformException: An error occurred while loading document 'data.xml'.
говорит вам, что он не пытается загрузить data.xml
в качестве документа таблицы стилей, а не как XML-документ!
Вы говорите: "Мне кажется, что, используя скомпилированное преобразование, резольвер не вызывается", но при условии, что вы можете отлаживать только XmlStaticResolver.ResolveUri()
останова XmlStaticResolver.ResolveUri()
и видеть, где она вызывается и с какими аргументами.