2008-10-08 10 views
35

Próbuję przekonwertować plik XML na znaczniki używane przez dokuwiki, używając XSLT. To faktycznie działa do pewnego stopnia, ale wcięcie w pliku XSL jest wstawiane do wyników. W tej chwili mam dwie możliwości: całkowicie zrezygnować z tego XSLT i znaleźć inny sposób konwersji z XML na znaczniki dokuwiki, lub usunąć około 95% białych znaków z pliku XSL, czyniąc go prawie nieczytelnym i koszmarem utrzymania.Konwersja XML na zwykły tekst - jak powinienem zignorować/obsłużyć białe znaki w XSLT?

Czy istnieje sposób na zachowanie wcięcia w pliku XSL bez przekazywania całego spacji do dokumentu końcowego?

Tło: Przeprowadzam migrację narzędzia autodoc ze statycznych stron HTML na dokuwiki, dzięki czemu interfejs API opracowany przez zespół serwera może zostać udokumentowany przez zespół ds. Aplikacji za każdym razem, gdy zespół aplikacji wpadnie w źle udokumentowany kod. Logika polega na oddzieleniu sekcji każdej strony dla narzędzia autodoc i zezwoleniu na komentarze w dowolnym miejscu poza tym blokiem. Używam XSLT, ponieważ mamy już plik XSL do konwersji z XML na XHTML, i zakładam, że szybciej będzie przepisać XSL, niż uruchomić własne rozwiązanie od zera.

Edytuj: Ach, racja, głupcze mnie, zaniedbałem atrybut wcięć. (Inna uwaga w tle: jestem nowy w XSLT.) Z drugiej strony, wciąż muszę radzić sobie z nowymi liniami. Dokuwiki używa potoków do rozróżniania kolumn tabeli, co oznacza, że ​​wszystkie dane w wierszu tabeli muszą znajdować się w jednej linii. Czy istnieje sposób na powstrzymanie wysyłania nowych linii (tylko sporadycznie), więc mogę wykonać dość złożoną logikę dla każdej komórki tabeli w nieco czytelnej fasionie?

Odpowiedz

75

są trzy powody, dla dostaję niechciane spacje w wyniku transformacji XSLT:

  1. spacje, który pochodzi z między węzłami w dokumencie źródłowym
  2. spacje, która pochodzi z wewnątrz węzłów w dokumencie źródłowym
  3. spacje, że pochodzi z arkusza

mam zamiar mówić o wszystkie trzy ponieważ może być trudno powiedzieć, skąd pochodzi biała spacja, więc możesz potrzebować kilku strategii.

Aby rozwiązać odstępy między węzłami, które jest w dokumencie źródłowym, należy użyć <xsl:strip-space> wyrzucić wszelkie spacje, który pojawia się pomiędzy dwoma węzłami, a następnie użyj <xsl:preserve-space> zachować znaczną spacje, które mogą pojawić się w ciągu mieszanej zawartości.Na przykład, jeśli dokument źródłowy wygląda następująco:

<ul> 
    <li>This is an <strong>important</strong> <em>point</em></li> 
</ul> 

następnie będziemy chcieli, aby ignorować odstępy między <ul> i <li> oraz między </li> i </ul>, który nie jest istotny, ale zachować odstępy między <strong> i <em> elementów, które są ważne pod (w przeciwnym razie otrzymasz "Jest to ** ważny punkt ***"). W tym celu użyć

<xsl:strip-space elements="*" /> 
<xsl:preserve-space elements="li" /> 

Atrybut elements na <xsl:preserve-space> lista powinna w zasadzie wszystkie elementy w dokumencie, które zostały zmieszane treści.

bok: using <xsl:strip-space> także zmniejsza rozmiar drzewa źródłowego w pamięci i sprawia, że ​​arkusz stylów bardziej wydajne, więc warto robić, nawet jeśli nie masz problemów z białymi znakami tego rodzaju.

Aby zająć się białymi znakami pojawiającymi się w węzłach w dokumencie źródłowym, należy użyć normalize-space(). Na przykład, jeśli masz:

<dt> 
    a definition 
</dt> 

i można mieć pewność, że element <dt> nie posiada żadnych elementów, które chcesz zrobić coś, czym można zrobić:

<xsl:template match="dt"> 
    ... 
    <xsl:value-of select="normalize-space(.)" /> 
    ... 
</xsl:template> 

wiodące i końcowe białe znaki zostaną usunięte z wartości elementu <dt>, a otrzymasz ciąg znaków "a definition".

Aby rozwiązać spacje pochodzących z arkusza stylów, który jest chyba jednym jesteś doświadcza, kiedy masz tekst w szablonie jak ten:

<xsl:template match="name"> 
    Name: 
    <xsl:value-of select="." /> 
</xsl:template> 

XSLT arkusze stylów są przetwarzane w taki sam sposób jak przetwarzane dokumenty źródłowe, dlatego powyższy kod XSLT jest interpretowany jako drzewo z elementem <xsl:template> z atrybutem match, którego pierwszym elementem potomnym jest węzeł tekstowy, a drugim elementem potomnym jest element <xsl:value-of> z atrybutem select. Węzeł tekstowy ma wiodące i końcowe białe znaki (w tym podziały wierszy); ponieważ jest dosłownym tekstem w arkuszu stylów, zostaje dosłownie skopiowany do wyniku, ze wszystkimi wiodącymi i ciągłymi odstępami.

Ale niektóre białe znaki w arkuszach stylów XSLT są automatycznie usuwane, a mianowicie między węzłami. W wyniku nie występuje podział wiersza, ponieważ występuje przerwa między wierszem <xsl:value-of> i zamknięciem linii <xsl:template>.

Aby uzyskać tylko tekst, który ma w efekcie wykorzystania elementu <xsl:text> takiego:

<xsl:template match="name"> 
    <xsl:text>Name: </xsl:text> 
    <xsl:value-of select="." /> 
</xsl:template> 

Procesor XSLT ignoruje podziały wiersza i wcięcia, które pojawiają się między węzłami, a tylko wyjście tekstu w element <xsl:text>.

+0

To było bardzo pomocne! Dzięki. – Black

+0

, który był rzeczywiście pomocny, ale jestem zaintrygowany przez użycie frazy "między węzłami". Czy nie jest prawdą, że wszystkie białe spacje są zawarte w węzłach tekstowych? Co masz na myśli przez "między węzłami"? Gdybym nie rozpoznał twojego nazwiska, założyłbym, że potrzebowałeś wykładu na temat struktury dokumentu XML. – LarsH

+0

Dobry artykuł, dzięki! Ale mówiąc ściśle, używasz terminu "węzeł", gdzie faktycznie masz na myśli "element". – rustyx

4

Czy używasz indent = "no" w tagu wyjściowym?

<xsl:output method="text" indent="no" /> 

Także jeśli używasz xsl: value-of można użyć wyłączyć-Output-cytowanie = "yes", aby pomóc w pewnych kwestiach białych.

+4

Przez większość czasu używanie 'disable-output-escaping' jest złym sposobem robienia rzeczy. Jest tam tylko w bardzo ograniczonych sytuacjach. Opowiadanie d-o-e w taki ogólny sposób komuś, kto nie wie lepiej, jest prawdopodobnie bardziej szkodliwy niż pomocny. Zobacz http://www.dpawson.co.uk/xsl/sect2/N2215.html#d3702e223 – LarsH

0

chodzi o edycję o nowych linii, można użyć tego szablonu do rekurencyjnie zastąpić jeden ciąg w innym ciągiem, i można go używać do przerw linia:

<xsl:template name="replace.string.section"> 
    <xsl:param name="in.string"/> 
    <xsl:param name="in.characters"/> 
    <xsl:param name="out.characters"/> 
    <xsl:choose> 
    <xsl:when test="contains($in.string,$in.characters)"> 
     <xsl:value-of select="concat(substring-before($in.string,$in.characters),$out.characters)"/> 
     <xsl:call-template name="replace.string.section"> 
     <xsl:with-param name="in.string" select="substring-after($in.string,$in.characters)"/> 
     <xsl:with-param name="in.characters" select="$in.characters"/> 
     <xsl:with-param name="out.characters" select="$out.characters"/> 
     </xsl:call-template> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:value-of select="$in.string"/> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

nazwać to w następujący sposób (w tym przykładzie zastępuje linię przerwy w zmiennej $ some.string z miejsca):

<xsl:call-template name="replace.string.section"> 
     <xsl:with-param name="in.string" select="$some.string"/> 
     <xsl:with-param name="in.characters" select="'&#xA;'"/> 
     <xsl:with-param name="out.characters" select="' '"/> 
    </xsl:call-template> 
3

@ Odpowiedź JeniT jest świetna, chcę tylko wskazać sztuczkę do zarządzania białymi znakami. Nie jestem pewien, czy to najlepszy sposób (lub nawet dobry sposób), ale na razie działa dla mnie.

(. "S" przestrzeni "E" pusty "N" dla nowej linii)

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE xsl:transform [ 
    <!ENTITY s "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" > 
    <!ENTITY s2 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" > 
    <!ENTITY s4 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" > 
    <!ENTITY s6 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>  </xsl:text>" > 
    <!ENTITY e "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'></xsl:text>" > 
    <!ENTITY n "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> 
</xsl:text>" > 
]> 

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
<xsl:output method="text"/> 
<xsl:template match="/"> 
    &e;Flush left, despite the indentation.&n; 
    &e; This line will be output indented two spaces.&n; 

     <!-- the blank lines above/below won't be output --> 

    <xsl:for-each select="//foo"> 
    &e; Starts with two blanks: <xsl:value-of select="@bar"/>.&n; 
    &e; <xsl:value-of select="@baz"/> The 'e' trick won't work here.&n; 
    &s2;<xsl:value-of select="@baz"/> Use s2 instead.&n; 
    &s2; <xsl:value-of select="@abc"/> <xsl:value-of select="@xyz"/>&n; 
    &s2; <xsl:value-of select="@abc"/>&s;<xsl:value-of select="@xyz"/>&n; 
    </xsl:for-each> 
</xsl:template> 
</xsl:transform> 

stosować do:

<?xml version="1.0" encoding="UTF-8"?> 
<foo bar="bar" baz="baz" abc="abc" xyz="xyz"></foo> 

wyjść:

Flush left, despite the indentation. 
    This line will be output indented two spaces. 
    Starts with two blanks: bar. 
baz The 'e' trick won't work here. 
    baz Use s2 instead. 
    abcxyz 
    abc xyz 

Sztuczka "e" działa przed węzłem tekstowym zawierającym co najmniej jeden znak spoza białych znaków, ponieważ rozwija się do tego:

<xsl:template match="/"> 
    <xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text> 
</xsl:text> 

Od rules for stripping whitespace powiedzieć, że białe znaki tylko węzły tekstowe uzyskać uproszczoną, nowej linii i wcięcia między < xsl: template > i < xsl: text > zdjęte (dobry). Ponieważ reguły mówią, że węzeł tekstowy z co najmniej jednym znakiem odstępu jest zachowany, niejawny węzeł tekstowy zawierający " This line will be output indented two spaces." zachowuje wiodącą białą przestrzeń (ale myślę, że to zależy również od ustawień dla paska/zachowania/normalizacji). "& n;" na końcu linii wstawia znak nowej linii, ale zapewnia również, że wszelkie następne spacje są ignorowane, ponieważ pojawiają się między dwoma węzłami.

Problem polega na tym, że chcę wyprowadzić wciętą linię zaczynającą się od < xsl: value-of >. W takim przypadku "& e;" nie pomoże, ponieważ wcięcia nie są "dołączone" do żadnych znaków spoza białych znaków. Tak więc w tych przypadkach używam "& s2;" lub "& s4;", w zależności od tego, ile chcę wcięcia.

Jest brzydki hack, jestem pewien, ale przynajmniej nie muszę się rozwlekły „< xsl: tekstowy >” tags zaśmiecanie mojego XSLT, a przynajmniej mogę jeszcze wciąć samego XSLT, więc jest to czytelne. Czuję się, jakbym nadużywał XSLT do czegoś, do czego nie był przeznaczony (przetwarzanie tekstu) i jest to najlepsze, co mogę zrobić.


Edit: W odpowiedzi na komentarze, to jak to wygląda bez „makr”:

<xsl:template match="/"> 
    <xsl:text>Flush left, despite the indentation.</xsl:text> 
    <xsl:text> This line will be output indented two spaces.</xsl:text> 
    <xsl:for-each select="//foo"> 
    <xsl:text> Starts with two blanks: </xsl:text><xsl:value-of select="@bar"/>.<xsl:text> 
</xsl:text> 
    <xsl:text> </xsl:text><xsl:value-of select="@abc"/><xsl:text> </xsl:text><xsl:value-of select="@xyz"/><xsl:text> 
</xsl:text> 
    </xsl:for-each> 
</xsl:template> 

myślę, że sprawia, że ​​mniej Można zobaczyć zamierzonego wcięcia wyjściowego, i wkręca wcięcie samego XSL, ponieważ znaczniki końcowe </xsl:text> muszą pojawić się w kolumnie 1 pliku XSL (w przeciwnym razie w pliku wynikowym pojawią się niepożądane spacje).

+0

@Dan: Po pierwsze,' xsl: text' nie jest verbose i zawsze możesz użyć concat na 'xsl: value Po drugie, nie przetwarzasz tekstu, twoje wyjście jest zwykłym tekstem –

+0

@Dan: Last. Twoje rozwiązanie jest przeciwko XSLT, ponieważ te elementy (odpowiednio zadeklarowane) są częścią składni powierzchni dokumentu XML (arkusz stylów, w takim przypadku). Tak więc zastąpienie zajmuje czas w analizie fase, zanim dotrze do procesora XSLT Po dokonaniu zamiany i pojawieniu się ** nowych elementów ** w arkuszu stylów, reguły usuwania/zachowywania białych tekstowych węzłów Z punktu widzenia czytelnika nie będzie jasne, jaki byłby wynik twojego arkusza stylów. –

+0

@Alejandro: dziękuję za informację zwrotną.Myślę, że nie jest to pełne gadżetowanie, jeśli jesteś już przyzwyczajony do XML ... moje tło jest bardziej lex/yacc/C++, więc zdecydowanie czuję się mojego elementu tutaj. Przypuszczam, że użycie edytora XML w porównaniu z edytorem tekstu może pomóc. – Dan

Powiązane problemy