2009-06-15 13 views
5

.NET 2.0/VS2005Jak rozwiązać XSL zawiera transformacja, która ładuje XSL z ciągu?

Próbuję użyć klasy XslCompiledTransform do przeprowadzenia transformacji XSL. Mam dwa pliki XSL, z których pierwsza zawiera odniesienie do innego w formie oświadczenia <xsl:include>:

Main.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:include href="Included.xsl" /> 
    ... 
    ... 
</xsl:stylesheet> 

Teraz, jeśli mogę załadować „Main .xsl”złożyć się jako URI, mój kod transformacja będzie tak proste, jak:

// 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(); 
} 

problemem jest to, że mogę otrzymać zawartość pliku Main.xsl jako ciąg i trzeba załadować ciąg w postaci XmlReader/IXpathNavigable . Jest to niezbędne ograniczenie w tej chwili. Kiedy próbuję zrobić to samo używając XmlReader/XpathDocument, to się nie powiedzie, ponieważ kod wyszukuje "Included.xsl" w folderze C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\! Oczywiście, XmlResolver nie jest w stanie rozwiązać względnego adresu URL, ponieważ odbiera tylko ciąg znaków jako wejściowy XSL.

Moje wysiłki w tym kierunku wyglądać następująco:

// 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(); 
} 

Próbowałem użyć sposobu XmlUrlResolver ResolveUri do uzyskania Stream przedstawieniu plik XSL, które należy uwzględnić, ale jestem zdezorientowany, jak korzystać z tego strumienia. IOW, jak mogę powiedzieć obiekt XslCompiledTransform używać tego strumienia oprócz Main.xsl XmlReader:

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?? 


Każda pomoc jest mile widziana. Przepraszamy za długi post!

Dla odniesienia, Wyjątek StackTrace wygląda następująco:

[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 
+0

Pracuję nad czymś podobnym do tego, czego wymaga twoje pytanie i znalazłem artykuł MSDN - [Resolving the Unknown: Building Custom XmlResolvers w .NET Framework] (http://msdn.microsoft.com/en-us/ library/aa302284.aspx) - wydaje się to być bardzo obiecującym rozwiązaniem. –

Odpowiedz

2

jestem prawdopodobnie brakuje oczywiste, ale to nie powód, że nie wystarczy zmienić URI Included.xsl być prawdziwym URL ? Może to zrobić w dokumencie XSL, jeśli masz dostęp lub manipulujesz ciągami w inny sposób?

+0

David: Dzięki za odpowiedź. Powodem jest to, że nie mogę na sztywno zakodować żadnych ścieżek w dowolnym miejscu w aplikacji, co do zasady. W tym przypadku byłaby to moja ostatnia deska ratunku. ;-) – Cerebrus

+0

Nie jestem pewien, czy można tego uniknąć. Przykład strumienia działa, ponieważ ładujesz plik Main.xsl z tej samej fizycznej lokalizacji co plik Include.xsl. Stąd wracając do manipulowania strunami można po prostu dodać Request.PhysicalApplication Path do URI W przeciwnym razie, w jaki sposób kod będzie wiedział, gdzie szukać Include.xsl? Zawsze potrzebny będzie dodatkowy wskaźnik, ponieważ pochodzi on z ciągu Tnx –

+0

Hmmm ... Nie byłem w stanie tego zrobić przez wyprowadzenie Custom XmlUrlResolver (który był czystszy sposób). Z powodu ograniczeń czasowych, będę musiał to zrobić poprzez manipulację String. Dzięki za pomysł. – Cerebrus

5

użyć niestandardowego 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); 
     } 
    } 

i używać go w funkcji obciążenia XslCompiledTransform,

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

Karthik: Dzięki za odpowiedź. To kierunek, w którym obecnie zmierzam. Zastanawiam się, czy istnieje sposób na uniknięcie twardego kodowania części "http: // mypath /" w niestandardowym XmlUrlResolver. Jakieś pomysły ? – Cerebrus

+0

Może to być parametr konfigurowalny lub jeśli jest hostowany na tym samym serwerze, użyj Server.MapPath. BTW, Jak zdobyć Main.xsl? Uzyskując dostęp do ścieżki HTTP? – Gee

2

Jako odpowiedź Gee wspomina, chcesz użyć niestandardowego XmlResolver (którego XmlUrlResolver już pochodzące), ale jeśli zastąpisz metodę GetEntity, możesz rozwiązać odniesienia w głównym dokumencie XSLT w zabawny i interesujący sposób. Celowo prosty przykład tego, jak można rozwiązać odniesienie do 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); 
    } 
} 

Podczas ładowania Menem.xsl dokument chcesz zmienić odpowiedni kod do następujących:

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

Powyższy przykład został na podstawie informacji wybrałem się w artykule MSDN Resolving the Unknown: Building Custom XmlResolvers in the .NET Framework.

0

mam już sukces z wykorzystaniem przekształceń robi wszystko w pamięci:

Posiadanie XSLT zawierające następujące obejmuje:

import href = "Common.xslt" i import 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 ""; 
      } 
     } 

Zauważ, że absolutnie wszystkie treści jest jako ciągi: stylesheet, xmlToParse i treści „wspólne” i „import” w formacie XHTML