XML을 일반 텍스트로 변환 - XSLT에서 공백을 어떻게 무시/처리해야 합니까?

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

  •  06-07-2019
  •  | 
  •  

문제

XSLT를 사용하여 XML 파일을 dokuwiki에서 사용하는 마크업으로 변환하려고 합니다.이는 실제로 어느 정도 작동하지만 XSL 파일의 들여쓰기가 결과에 삽입됩니다.현재로서는 두 가지 선택이 있습니다.이 XSLT를 완전히 버리고 XML을 dokuwiki 마크업으로 변환하는 다른 방법을 찾거나 XSL 파일에서 공백의 약 95%를 삭제하여 거의 읽을 수 없게 만들고 유지 관리가 악몽이 되도록 만듭니다.

최종 문서에 공백을 모두 전달하지 않고 XSL 파일에서 들여쓰기를 유지하는 방법이 있습니까?

배경:저는 autodoc 도구를 정적 HTML 페이지에서 dokuwiki로 마이그레이션할 예정입니다. 그러면 앱 팀이 제대로 문서화되지 않은 코드를 실행할 때마다 서버 팀에서 개발한 API를 애플리케이션 팀에서 추가로 문서화할 수 있습니다.논리는 각 페이지의 섹션을 autodoc 도구용으로 따로 설정하고 이 블록 외부의 어디든 주석을 허용하는 것입니다.XML에서 XHTML로 변환할 XSL 파일이 이미 있기 때문에 XSLT를 사용하고 있으며, 자체 솔루션을 처음부터 시작하는 것보다 XSL을 다시 작성하는 것이 더 빠를 것이라고 가정합니다.

편집하다:아, 그렇군요. 제가 들여쓰기 속성을 무시했군요.(기타 배경 참고 사항:저는 XSLT를 처음 접했습니다.) 반면에 여전히 개행 문자를 처리해야 합니다.Dokuwiki는 테이블 열을 구별하기 위해 파이프를 사용합니다. 이는 테이블 행의 모든 ​​데이터가 한 행에 있어야 함을 의미합니다.(가끔) 개행 문자가 출력되는 것을 억제하여 각 테이블 셀에 대해 다소 읽기 쉬운 방식으로 상당히 복잡한 논리를 수행할 수 있는 방법이 있습니까?

도움이 되었습니까?

해결책

XSLT 변환 결과 원하지 않는 공백이 발생하는 세 가지 이유는 다음과 같습니다.

  1. 소스 문서의 노드 사이에서 나오는 공백
  2. 소스 문서의 노드 내에서 나오는 공백
  3. 스타일시트에서 나오는 공백

공백이 어디서 오는지 파악하기 어렵고 여러 가지 전략을 사용해야 할 수도 있으므로 세 가지 모두에 대해 이야기하겠습니다.

소스 문서의 노드 사이에 있는 공백을 처리하려면 다음을 사용해야 합니다. <xsl:strip-space> 두 노드 사이에 나타나는 공백을 제거한 다음 <xsl:preserve-space> 혼합 콘텐츠 내에 나타날 수 있는 중요한 공백을 보존합니다.예를 들어 소스 문서가 다음과 같은 경우:

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

그런 다음 사이의 공백을 무시하고 싶을 것입니다. <ul> 그리고 <li> 그리고 그 사이에 </li> 그리고 </ul>, 이는 중요하지 않지만, <strong> 그리고 <em> 요소, 이는 ~이다 중요합니다(그렇지 않으면 "**중요***포인트*입니다"라는 메시지가 표시됩니다).이를 수행하려면

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

그만큼 elements 속성 <xsl:preserve-space> 기본적으로 콘텐츠가 혼합된 문서의 모든 요소를 ​​나열해야 합니다.

곁에:사용하여 <xsl:strip-space> 또한 메모리의 소스 트리 크기를 줄이고 스타일시트를 더욱 효율적으로 만들기 때문에 이런 종류의 공백 문제가 없더라도 수행할 가치가 있습니다.

소스 문서의 노드 내에 나타나는 공백을 처리하려면 다음을 사용해야 합니다. normalize-space().예를 들어 다음과 같은 경우가 있습니다.

<dt>
  a definition
</dt>

그리고 당신은 확신할 수 있습니다 <dt> 요소에는 작업하려는 요소가 포함되어 있지 않은 경우 다음을 수행할 수 있습니다.

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

선행 및 후행 공백은 값에서 제거됩니다. <dt> 요소를 사용하면 문자열을 얻을 수 있습니다. "a definition".

아마도 여러분이 경험하고 있는 스타일시트에서 오는 공백을 해결하려면 템플릿 내에 다음과 같은 텍스트가 있는 경우입니다.

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

XSLT 스타일시트는 처리하는 소스 문서와 동일한 방식으로 구문 분석되므로 위의 XSLT는 <xsl:template> 요소 match 첫 번째 하위 항목이 텍스트 노드이고 두 번째 하위 항목이 텍스트 노드인 속성 <xsl:value-of> 요소 select 기인하다.텍스트 노드에는 선행 및 후행 공백(줄 바꿈 포함)이 있습니다.스타일시트의 문자 그대로의 텍스트이기 때문에 모든 선행 및 후행 공백과 함께 문자 그대로 결과에 복사됩니다.

하지만 일부 XSLT 스타일시트의 공백, 즉 노드 사이의 공백은 자동으로 제거됩니다.결과 사이에 줄 바꿈이 있으므로 결과에 줄 바꿈이 표시되지 않습니다. <xsl:value-of> 그리고 마감일은 <xsl:template>.

결과에서 원하는 텍스트만 얻으려면 <xsl:text> 다음과 같은 요소:

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

XSLT 프로세서는 노드 사이에 나타나는 줄 바꿈 및 들여쓰기를 무시하고 텍스트 내의 텍스트만 출력합니다. <xsl:text> 요소.

다른 팁

출력 태그에 Indent = "No"를 사용하고 있습니까?

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

또한 XSL을 사용하는 경우 값을 사용하는 경우 비활성화 출력 전달 = "예"를 사용하여 일부 흰색 스페이스 문제를 돕습니다.

@jenit의 대답은 훌륭합니다. 나는 공백을 관리하기위한 트릭을 지적하고 싶습니다. 나는 그것이 최선의 방법 (또는 좋은 방법)인지 확실하지는 않지만 지금은 나에게 효과적입니다.

( "S"공간, "E", 빈의 경우 "N"은 Newline의 경우.)

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

적용 : :

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

출력 :

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

'e'트릭은 텍스트 노드가 하나 이상의 비 whitesce 문자를 포함하는 텍스트 노드 전에 작동합니다.

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

이후 공백을 벗기는 규칙 공백 전용 텍스트 노드가 박탈되고, Newline 및 Intentation간에u003Cxsl:template> 그리고u003Cxsl:text> 벗겨지기 (좋은). 규칙에 따라 하나 이상의 공백 문자가있는 텍스트 노드가 보존되기 때문에 암시 적 텍스트 노드가 포함되어 있습니다. " This line will be output indented two spaces." 선행 공백을 유지합니다 (그러나 이것은 스트립/보존/정규화에 대한 설정에 따라 다릅니다). 그 다음에;" 라인의 끝에서는 새로운 라인을 삽입하지만 두 노드 사이에 나타나기 때문에 다음과 같은 공백이 무시되도록합니다.

내가 가진 문제는 내가u003Cxsl:value-of> . 이 경우 "& E" 들여 쓰기 공백은 비 whitesce 캐릭터에 "첨부"되지 않기 때문에 도움이되지 않습니다. 그러한 경우, 나는 "& s2;"를 사용합니다. 또는 "& s4;", 내가 원하는 들여 쓰기에 따라.

그것은 추악한 해킹입니다. 그러나 적어도 나는 장점이 없습니다. "u003Cxsl:text> "내 XSLT를 쓰레기를 쓰다듬고 적어도 나는 여전히 XSLT 자체를 들여 쓰기를 할 수 있으므로 읽을 수 있습니다. XSLT가 (텍스트 처리) 용으로 설계되지 않은 것에 대해 학대하고있는 것 같습니다. 이것이 내가 할 수있는 최선입니다.


편집하다:의견에 대한 응답으로 이것은 "매크로"가없는 모습입니다.

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

나는 그것이 의도 된 출력 들여 쓰기를 보는 것이 명확하지 않다고 생각하며, XSL 자체의 압입을 망칩니다. </xsl:text> 엔드 태그는 XSL 파일의 열 1 열에 나타나야합니다 (그렇지 않으면 출력 파일에서 바람직하지 않은 공백이 나타납니다).

새 라인에 대한 편집과 관련 하여이 템플릿을 사용하여 다른 문자열 내에서 하나의 문자열을 재귀 적으로 교체 할 수 있으며 라인 브레이크에 사용할 수 있습니다.

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

다음과 같이 호출합니다 (이 예제는 $ some.string 변수의 라인 브레이크를 공간으로 대체합니다) :

    <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>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top