Как я могу написать текстовый узел только для пробелов в выводе XML XSLT - PullRequest
0 голосов
/ 29 октября 2018

У нас есть конвейер обмена сообщениями, который включает преобразования XML в XML.

Для такого исходного документа (который также может быть в одной строке без форматирования):

<doc>
  <a>Foo</a>
  <b>Bar1</b>
  <b>Bar2</b>
  <b>Bar3</b>
  <c>Baz</c>
</doc>

Мне нужен вывод XML преобразования (обратите внимание на разрывы строк):

<x>Bar1
Bar2
Bar3</x>

Но вывод, который я получаю:

<x>Bar1Bar2Bar3</x>

Таблица стилей выглядит следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />

  <xsl:template match="/">
    <x>
      <xsl:for-each select="//b">
        <xsl:value-of select="." />
        <xsl:if test="position() != last()">
          <xsl:text>&#xD;&#xA;</xsl:text>  <!-- something wrong here? -->
        </xsl:if>
      </xsl:for-each>
    </x>
  </xsl:template>
</xsl:stylesheet>

Если я добавлю непробельный символ к текстовому узлу, я получу сохранение новой строки. Итак, если я изменю узел xsl:text на (обратите внимание на добавленный дефис):

<xsl:text>-&#xD;&#xA;</xsl:text>

тогда я получаю вывод:

<x>Bar1-
Bar2-
Bar3</x>

Как я могу сгенерировать желаемый результат?

Обратите внимание, что мы ограничены XSLT 1.0.

Обновление

Я сделал еще несколько тестов. Ниже приведен полный код для воспроизведения проблемы. Интересно, что этот код воспроизводит проблему при запуске под .Net Framework 4.5 и .Net Core 2.1, но он дает желаемый результат при запуске под Mono.

using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Xsl;

namespace xslt
{
    class Program
    {
        static void Main(string[] args)
        {
            var doc = new XmlDocument();
            doc.LoadXml(@"<doc><a>Foo</a><b>Bar1</b><b>Bar2</b><b>Bar3</b><c>Baz</c></doc>");

            var xsl = new XmlDocument();
            xsl.LoadXml(@"<?xml version='1.0' encoding='utf-8'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:output omit-xml-declaration='yes' method='xml' version='1.0' />

    <xsl:template match='/'>
        <x>
        <xsl:for-each select='//b'>
            <xsl:value-of select='.' />
            <xsl:if test='position() != last()'>
                <xsl:text>&#xD;&#xA;</xsl:text>  <!-- something wrong here? -->
            </xsl:if>
        </xsl:for-each>
        </x>
    </xsl:template>
</xsl:stylesheet>");

            var xslt = new XslCompiledTransform();
            xslt.Load(xsl);

            using (var stream = new MemoryStream())
            {
                xslt.Transform(doc, null, stream);
                Console.WriteLine(Encoding.UTF8.GetString(stream.ToArray()));
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Мне удалось добиться этого, добавив блок скриптов в таблицу стилей для построения значения, разделенного новой строкой.

Мне все еще интересно узнать, возможно ли это с чистым XSL.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp">
  <xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />

  <xsl:template match="/">
    <x>
      <xsl:value-of select='userCSharp:JoinLines(//b)' />
    </x>
  </xsl:template>

  <msxsl:script language="C#" implements-prefix="userCSharp">
    <![CDATA[

public string JoinLines(XPathNodeIterator nodes)
{
  var builder = new StringBuilder();
  while (nodes.MoveNext())
  {
    builder.AppendLine(nodes.Current.Value);
  }
  return builder.ToString().Trim();
}

    ]]>
  </msxsl:script>
</xsl:stylesheet>
0 голосов
/ 29 октября 2018

Как сохранить текстовый узел только для пробелов в выводе XML XSLT

Если вы действительно хотите сохранить узлы text() между элементами b, вы можете сопоставить их с выражением XPath

text()[preceding::*[1][self::b]][following::*[1][self::b]]

и скопируйте все содержимое с xsl:copy-of. Весь набор шаблонов может выглядеть так:

<xsl:template match="/doc">
    <x>
      <xsl:apply-templates select="node()|@*" />
    </x>
</xsl:template>

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

<xsl:template match="text()" />

<xsl:template match="text()[preceding::*[1][self::b]][following::*[1][self::b]]">
    <xsl:copy-of select="." />
</xsl:template>

Копирует также пробелы между ними, а не только переводы строк, поэтому вывод выглядит как

<x>Bar1-
  Bar2
  Bar3</x>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...