Convertendo XML em texto simples - como eu deveria ignorar / alça de espaço em branco na XSLT?

StackOverflow https://stackoverflow.com/questions/184431

  •  06-07-2019
  •  | 
  •  

Pergunta

Eu estou tentando converter um arquivo XML para a marcação usada por dokuwiki, usando XSLT. Isso realmente funciona até certo ponto, mas o recuo no arquivo XSL está sendo inseridos nos resultados. No momento, tenho duas opções: abandonar essa coisa XSLT inteiramente, e encontrar uma outra maneira de converter de XML para dokuwiki marcação ou excluir cerca de 95% do espaço em branco do arquivo XSL, tornando-o quase-ilegível e um pesadelo de manutenção.

Existe alguma maneira de manter o recuo no arquivo XSL sem passar tudo o que espaço em branco para o documento final?

Fundo: Estou migrando uma ferramenta autodoc de páginas HTML estáticas sobre a dokuwiki, então a API desenvolvida pela equipe do servidor pode ser ainda documentado pela equipe aplicações sempre que os aplicativos da equipe é executado em um código mal-documentado. A lógica é ter uma seção de cada página reservada para a ferramenta autodoc, e permitir comentários em qualquer lugar fora deste bloco. Estou usando XSLT porque já temos o arquivo XSL para converter de XML para XHTML, e eu estou supondo que ele será mais rápido para reescrever o XSL do que para fazer a minha própria solução a partir do zero.

Editar: Ah, certo, me tola, eu negligenciei o atributo travessão. (Outra nota de fundo:. Eu sou novo para XSLT) Por outro lado, eu ainda tenho que lidar com novas linhas. Dokuwiki utiliza tubos para diferenciar entre as colunas da tabela, o que significa que todos os dados de uma linha da tabela deve estar em uma linha. Existe uma maneira de novas linhas suprimir a ser emitido (apenas ocasionalmente), para que eu possa fazer alguma lógica bastante complexa para cada célula da tabela em um fasion pouco legível?

Foi útil?

Solução

Há três razões para obter espaço em branco indesejado no resultado de uma transformação XSLT:

  1. espaço em branco que vem entre nós no documento de origem
  2. espaço em branco que vem de dentro nós no documento de origem
  3. espaço em branco que vem do estilo

Vou falar sobre todos os três, porque pode ser difícil dizer onde o espaço em branco vem de modo que você pode precisar usar várias estratégias.

Para abordar o espaço em branco que está entre nós em seu documento de origem, você deve usar <xsl:strip-space> para retirar qualquer espaço em branco que aparece entre dois nós, e então usar <xsl:preserve-space> para preservar o espaço em branco significativo que pode aparecer dentro de conteúdo misto. Por exemplo, se sua fonte de documentos se parece com:

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

então você vai querer ignorar o espaço em branco entre o <ul> eo <li> e entre o </li> eo </ul>, o que não é significativo, mas preservar o espaço em branco entre os elementos <strong> e <em>, que é significativa (caso contrário você deseja obter "Este é um ** importante *** ponto *"). Para fazer isso use

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

O atributo elements em <xsl:preserve-space> deve listar basicamente todos os elementos no documento que têm conteúdo misto.

Além:. Utilizando <xsl:strip-space> também reduz o tamanho da árvore de origem na memória, e faz o seu estilo mais eficiente, por isso vale a pena fazer, mesmo se você não tem espaço em branco problemas deste tipo

Para abordar o espaço em branco que aparece dentro de nós em seu documento de origem, você deve usar normalize-space(). Por exemplo, se você tem:

<dt>
  a definition
</dt>

e você pode ter certeza que o elemento <dt> não terá quaisquer elementos que você quer fazer algo com, então você pode fazer:

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

O espaço em branco esquerda e à direita serão retirados do valor do elemento <dt> e você só vai conseguir o "a definition" string.

Para endereço espaço em branco que vem da folha de estilo, que é talvez o que você está experimentando, é quando você tem o texto dentro de um modelo como este:

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

folhas de estilo XSLT são analisadas da mesma forma como os documentos de origem que eles processam, assim que o acima XSLT é interpretado como uma árvore que detém um elemento <xsl:template> com um atributo match cujo primeiro filho é um nó de texto e cujo segundo filho é um <xsl:value-of> elemento com um atributo select. O nó de texto tem líder e espaços em branco (incluindo quebras de linha); uma vez que do texto literal na folha de estilo, ele é literalmente copiado para o resultado, com todo o espaço em branco esquerda e à direita.

e alguns espaços em branco em folhas de estilo XSLT se despojado automaticamente, ou seja, aqueles entre nós. Você não consegue uma quebra de linha no seu resultado, porque há uma quebra de linha entre o <xsl:value-of> eo encerramento do <xsl:template>.

Para obter apenas o texto que deseja no resultado, use o elemento <xsl:text> assim:

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

O processador XSLT irá ignorar as quebras de linha e recuo que aparecem entre nós, e só a saída do texto no elemento <xsl:text>.

Outras dicas

Você está usando travessão = "no" na sua tag de saída?

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

Além disso, se você estiver usando xsl:. Value-of você pode usar o disable-saída escapando = "yes" para ajudar com alguns problemas de espaço em branco

@ resposta de JeniT é grande, eu só quero salientar um truque para o gerenciamento de espaços em branco. Não tenho certeza de que é a melhor maneira (ou até mesmo uma boa maneira), mas funciona para mim por agora.

( "S" para o espaço, "E" para vazio, "n" para a nova linha.)

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

Applied a:

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

Saídas:

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

O truque 'e' funciona antes de um nó de texto contendo caracteres pelo menos um não-espaço em branco porque ele se expande para o seguinte:

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

Uma vez que as regras para descascar espaços em branco dizer que nós de texto somente espaços em branco se despojado, a nova linha e recuo entre o e se despojado (bom). Desde que as regras dizem que um nó de texto com pelo menos um espaço de caracteres é preservada, o nó de texto implícita contendo " This line will be output indented two spaces." mantém o seu espaço em branco à esquerda (mas acho que isso também depende das configurações de faixa / preservação / normalize). Então;" no final da linha insere uma nova linha, mas também garante que qualquer espaço em branco seguinte é ignorada, porque ele aparece entre dois nós.

O problema que tenho é quando eu quero a saída de uma linha recuada que começa com um . Nesse caso, o "& e;" não vai ajudar, porque o recuo espaços em branco não está "ligado" para quaisquer caracteres não-espaço em branco. Portanto, para aqueles casos, eu uso "& s2;" ou "& S4;"., dependendo da quantidade de recuo Quero

É um corte feio tenho certeza, mas pelo menos eu não tenho o detalhado "" tags de desarrumar a minha XSLT, e pelo menos eu ainda pode recuar o próprio XSLT por isso é legível. Eu sinto que estou abusando XSLT para algo que não foi projetado para (processamento de texto) e este é o melhor que posso fazer.


Editar: Em resposta aos comentários, este é o que parece, sem os "macros":

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

Eu acho que o torna menos claro para ver o recuo resultado pretendido, e isso estraga o recuo do próprio XSL porque as tags de fim </xsl:text> tem que aparecer na coluna 1 do arquivo XSL (caso contrário, você começa espaço em branco indesejado na arquivo de saída).

No que diz respeito a sua edição sobre as novas linhas, você pode usar este modelo para substituir recursivamente uma corda dentro de outra cadeia, e você pode usá-lo para quebras de linha:

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

Chamá-lo da seguinte forma (este exemplo substitui as quebras de linha na variável some.string $ com um espaço):

    <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>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top