2012-01-24 17 views
5

Como parte del proceso de compilación de nuestra aplicación web, configuré nuestras hojas de estilo XSLT para compilar con el compilador Microsoft's xsltc.exe cada vez que ejecutamos una compilación completa. Durante el desarrollo local, esto funcionó muy bien, ya que el código se compila y aloja en la misma ubicación. Sin embargo, una vez que esto se puso en el servidor de compilación, surgieron problemas.¿Cómo puedo resolver los elementos <xsl:import> y <xsl:include> con rutas relativas al usar xsltc.exe XslCompiledTransforms?

El servidor de compilación compilará las hojas de estilos XSLT tal como lo hago localmente, pero luego se ejecuta una secuencia de comandos que despliega el código compilado en nuestro servidor web de ensayo interno. Una vez que estos archivos binarios se han movido desde donde se compilaron, las rutas relativas en los elementos <xsl:import> y <xsl:include> ya no se resuelven correctamente, lo que provoca excepciones que se ven así cuando se ejecutan las hojas de estilo XSLT.

Could not find a part of the path 'e:\{PATH}\xslt\docbook\VERSION'. 
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
    at 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, Boolean useLongPath) 
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy) 
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize) 
    at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn) 
    at System.Xml.Xsl.Runtime.XmlQueryContext.GetDataSource(String uriRelative, String uriBase) 

Aquí es una idea general del código tal como está ahora:

var xslt = new XslCompiledTransform(); 
xslt.Load(typeof(Namespace.XslTransforms.CompiledXsltStylesheet)); 
xslt.Transform("input.xml", "output.xml"); 

En este momento estoy usando el método XslCompiledTransform.Load() con un único parámetro 'Tipo' para que en el xsltc.exe-based pre-compiled XSLT stylesheets. Puedo deducir por el seguimiento de la pila que .NET Framework está usando XmlUrlResolver para tratar de resolver la ubicación real de estas hojas de estilo externas, pero no veo una manera de proporcionar una implementación anulada de XmlResolver donde podría pasar una nueva baseUri que apunta a dónde viven estas hojas de estilo en el servidor web.

Supongo que puedo resolver esto ya no compilando previamente con xsltc.exe y cargando las hojas de estilo XSLT a través de XmlReaders, ya que eso me permitirá usar el other XslCompiledTransform.Load() methods que tiene un parámetro donde podría proporcionar mi propia implementación de XmlResolver. Sin embargo, me gusta la opción de compilación previa para la validación de la sintaxis y el rendimiento, por lo que no quiero renunciar a ella a menos que sea absolutamente necesario.

¿Hay una manera de utilizar xsltc.exe comprobar la validez de recopilar estas hojas de estilo XSLT, y aún así proporcionar una manera de indicar explícitamente el baseURI para la resolución de la ruta relativa <xsl:include> y <xsl:import> elementos en tiempo de ejecución?

+0

¿Las hojas de estilo importadas/incluidas no se implementan junto con el binario (en el "servidor web de ensayo interno")? –

+0

Lo son, pero están en un directorio diferente de donde se compilaron. – Technetium

+1

Si imita la estructura del directorio del servidor web de ensayo en el servidor de compilación (donde está compilando la hoja de estilo), ¿hará eso una diferencia positiva? –

Respuesta

3

Después de un mucho de jugar alrededor con esto, descubrí que tenía razón en que el código que proporcioné automáticamente usa el System.Xml.XmlUrlResolver para resolver las rutas relativas <xsl:include> y <xsl:import> en tiempo de ejecución. Sin embargo, el uso de XmlUrlResolver no está vinculado al System.Xml.XslCompiledTransform cuando se coloca en un archivo binario por xsltc.exe. El XmlResolver es realmente elegido por la propiedad XmlResolver en el System.Xml.XmlReaderSettings en el System.Xml.XmlReader que realiza la transformación en tiempo de ejecución. Una vez que configuré mi propio XmlResolver personalizado en XsltReaderSettings que estaba usando, pude controlar la resolución relativa de la ruta.

Si desea anular esta XmlResolver como lo hice, el código siguiente se puede utilizar como guía:

var customXmlResolver = new SomeCustomXmlResolver(); // Derives from XmlResolver 
var xmlReaderSettings = new XmlReaderSettings { 
    XmlResolver = customXmlResolver 
}; 

var xslt = new XslCompiledTransform(); 
xslt.Load(typeof(Namespace.XslTransforms.CompiledXsltStylesheet)); 

using (var xmlReader = XmlReader.Create("input.xml", xmlReaderSettings)) { 
    using (var xmlWriter = XmlWriter.Create("output.xml")) { 
    xslt.Transform(xmlReader, null, xmlWriter, customXmlResolver); 
    } 
} 

Sigo usando xsltc.exe para compilar mis hojas de estilo XSLT, pero cuando cargo estas hojas de estilo compiladas en el servidor web, el SomeCustomXmlResolver inyectado reescribe las rutas en los métodos ResolveUri() y GetEntity() reemplazados para que puedan encontrarse los archivos referenciados que viven en las rutas relativas basadas en <xsl:include> y <xsl:import>. Como una ventaja adicional, al agregar el mismo XmlResolver al final del método Transform(), las operaciones en el XML también tendrán sus rutas relativas resueltas correctamente.

+0

Debe estar muy confundido. El 'XmlReader' en su código se usa solo para leer' 'input.xml" '- no para cargar la hoja de estilo XSLT. Cuando se ejecuta el método 'xslt.Load()', no tiene ninguna referencia a ningún 'XmlReader'. Si hay algún problema con la ejecución de 'xsl: import' y' xsl: include', el método 'Load()' genera una excepción, y esto no ocurre en su caso, sin utilizar ninguna referencia al 'XmlReader '. –

+0

No estoy confundido Dimitre. Tengo código de trabajo y paso por un depurador. Puedo ver claramente que estas rutas relativas de los elementos XSL se pasan a través del XmlResolver I assign en tiempo de ejecución. '', '', ' ', etc. Esta es la respuesta correcta. – Technetium

+0

Technetium: tiene al menos una de las hojas de estilo importadas/incluidas que usa la función 'document()' con una URL relativa o vacía; este es el problema real que nunca explicó. Si ninguna de las hojas de estilo importadas/incluidas hace referencia a la función 'document()', no habría ningún problema. Encontraré algo de tiempo libre al día siguiente para construir ejemplos que lo prueben. –

0

No sé si esto se rompe el sistema, pero que tal vez de

  1. compilar con xsltc.exe
  2. desplegar el binario
  3. cargar el binario con this Load()

usted

  1. desplegar las hojas de estilo, sin embargo, muchos están obligados con las directivas import/include
  2. carga de la hoja de estilo principal, con this Load(), especificando el sistema de resolución para la import/incldue

Parece que todavía obtendrá el beneficio de un "compilado" hoja de estilo, al menos en tiempo de ejecución.

+0

Definitivamente es posible para mí acceder a estas hojas de estilo XSLT directamente desde el sistema de archivos en el servidor web. Incluso con la hoja de estilo principal compilada con xsltc.exe, TENDRÁ que estar allí para que la transformación funcione. Sin embargo, a menos que haya algo que no entienda sobre el método Load() que especificó, compilará las hojas de estilo cada vez que se carguen en tiempo de ejecución, que era una de las cosas que intentaba evitar por motivos de rendimiento. Eso también significaría que tendría que degradar la validación de sintaxis al estado de Prueba de unidad. – Technetium

+0

Oh, pensé que la DLL se estaba cargando una vez, al principio. Sí, eso no funcionará entonces. –

2

¿Hay alguna manera de usar xsltc.exe para precompilar estas hojas de estilo XSLT, y aún así proporcionar una forma de declarar explícitamente la baseUri para la resolución relativa de ruta de <xsl:include> y <xsl:import> elementos en tiempo de ejecución?

Intente utilizar:

XslCompiledTransform.CompileToType()

Uno de los argumentos que este método estático acepta es:

XmlResolver stylesheetResolver 
+0

¡Ah sí! Esto parece muy prometedor. Lo probaré, y marcaré esto como la respuesta correcta una vez que tenga la oportunidad de verificarlo. La documentación de msdn incluso establece que 'xsltc.exe' es un envoltorio alrededor de esto, así que la gran pregunta que queda es si 'XmlResolver' almacena en caché la baseUri durante el tiempo de compilación o si el' XmlResolver' se ejecuta nuevamente durante el tiempo de ejecución (que es lo que yo quiero y, basado sobre el rastro de la pila, lo que supongo que sucederá). – Technetium

+0

@Technetium: Creo que no tiene sentido arreglar el resolver tan temprano en el momento de la compilación. Esto debería ser lo que buscas. –

+0

Como lo hice en la escuela secundaria, actué prematuramente aquí porque estaba muy emocionado. ** Esta es en realidad una respuesta incorrecta. ** Si bien Dimitre tiene razón en que el XmlResolver provisto en este método se usa para resolver los elementos '' y '' cuando se compila la hoja de estilos XSLT, no se usa nuevamente en tiempo de ejecución . El XmlReaderSettings del archivo XML fuente elige el XmlResolver utilizado en tiempo de ejecución. Ver mi respuesta para más detalles. – Technetium

Cuestiones relacionadas