Есть ли таблица стилей или инструмент командной строки Windows для управляемого XML-форматирования, в частности, установка атрибутов по одной строке?

Я ищу инструмент XSLT или командной строки (или код С#, который можно сделать в инструменте командной строки и т.д.) для Windows, который будет делать XML-печать. В частности, я хочу, чтобы у меня была возможность поместить атрибуты в одну строку, например:

<Node>
   <ChildNode 
      value1='5'
      value2='6'
      value3='happy' />
</Node>

Он не должен быть ТОЧНО подобным, но я хочу использовать его для XML файла, который имеет узлы с десятками атрибутов и распространяет их по нескольким строкам, что упрощает их чтение, редактирование и text-diff.

ПРИМЕЧАНИЕ. Я считаю, что моим предпочтительным решением является лист XSLT, который я могу пройти через метод С#, хотя инструмент командной строки Windows тоже хорош.

Ответ 1

Здесь небольшая С# -камера, которая может использоваться непосредственно вашим кодом или встроена в exe и вызывается в comand-строке как "myexe from.xml to.xml":

    using System.Xml;

    static void Main(string[] args)
    {
        XmlWriterSettings settings = new XmlWriterSettings {
            NewLineHandling = NewLineHandling.Entitize,
            NewLineOnAttributes = true, Indent = true, IndentChars = "  ",
            NewLineChars = Environment.NewLine
        };

        using (XmlReader reader = XmlReader.Create(args[0]))
        using (XmlWriter writer = XmlWriter.Create(args[1], settings)) {
            writer.WriteNode(reader, false);
            writer.Close();
        }
    }

Пример ввода:

<Node><ChildNode value1='5' value2='6' value3='happy' /></Node>

Пример вывода (обратите внимание, что вы можете удалить <?xml ... с помощью settings.OmitXmlDeclaration):

<?xml version="1.0" encoding="utf-8"?>
<Node>
  <ChildNode
    value1="5"
    value2="6"
    value3="happy" />
</Node>

Обратите внимание, что если вам нужна строка, а не запись в файл, просто замените ее StringBuilder:

StringBuilder sb = new StringBuilder();
using (XmlReader reader = XmlReader.Create(new StringReader(oldXml)))
using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
    writer.WriteNode(reader, false);
    writer.Close();
}
string newXml = sb.ToString();

Ответ 2

Здесь PowerShell script, чтобы сделать это. Он принимает следующий ввод:

<?xml version="1.0" encoding="utf-8"?>
<Node>
    <ChildNode value1="5" value2="6" value3="happy" />
</Node>

... и производит это как вывод:

<?xml version="1.0" encoding="utf-8"?>
<Node>
  <ChildNode
    value1="5"
    value2="6"
    value3="happy" />
</Node>

Здесь вы идете:

param(
    [string] $inputFile = $(throw "Please enter an input file name"),
    [string] $outputFile = $(throw "Please supply an output file name")
)

$data = [xml](Get-Content $inputFile)

$xws = new-object System.Xml.XmlWriterSettings
$xws.Indent = $true
$xws.IndentChars = "  "
$xws.NewLineOnAttributes = $true

$data.Save([Xml.XmlWriter]::Create($outputFile, $xws))

Возьмите этот script, сохраните его как C:\formatxml.ps1. Затем из приглашения PowerShell введите следующее:

C:\formatxml.ps1 C:\Path\To\UglyFile.xml C:\Path\To\NeatAndTidyFile.xml

Этот script в основном использует платформу .NET, поэтому вы можете легко перенести это в приложение С#.

ПРИМЕЧАНИЕ. Если вы еще не запускали сценарии из PowerShell, вам нужно выполнить следующую команду в расширенной подсказке PowerShell, прежде чем вы сможете выполнить script:

Set-ExecutionPolicy RemoteSigned

Вам нужно сделать это только один раз.

Я надеюсь, что это вам полезно.

Ответ 3

Попробуйте Tidy на SourceForge. Хотя он часто используется в [X] HTML, раньше я успешно использовал его в XML - просто убедитесь, что вы используете -xml.

http://tidy.sourceforge.net/#docs

Tidy читает файлы HTML, XHTML и XML и пишет очищенную разметку.... Для общих XML файлов Tidy ограничивается исправлением основных ошибок правильной формы и красивой печатью.

Люди перенесли на несколько платформ, и он доступен как исполняемая и вызываемая библиотека.

У Tidy есть куча вариантов, в том числе:

http://api.html-tidy.org/tidy/quickref_5.0.0.html#indent

отступ-атрибуты
Тип верха: логическое значение
По умолчанию: нет Пример: y/n, да/нет, t/f, true/false, 1/0
Эта опция указывает, должен ли Tidy начинать каждый атрибут с новой строки.

Одно предостережение:

Ограниченная поддержка XML

Процессоры XML, совместимые с рекомендацией W3C XML 1.0, очень требовательны к тому, какие файлы они будут принимать. Tidy может помочь вам исправить ошибки, которые приводят к отклонению ваших файлов XML. Tidy еще не распознает все возможности XML, например, он не понимает разделы CDATA или подмножества DTD.

Но я подозреваю, что если ваш XML не является действительно продвинутым, инструмент должен работать нормально.

Ответ 4

Существует инструмент, который может разделять атрибуты по одному в строке: xmlpp. Это perl script, поэтому вам нужно будет установить perl. Использование:

perl xmlpp.pl -t input.xml

Вы также можете определить порядок атрибутов, создав файл с именем attributeOrdering.txt и вызывая perl xmlpp.pl -s -t input.xml. Для получения дополнительных параметров используйте perl xmlpp.pl -h

Надеюсь, в нем не так много ошибок, но я работал до сих пор.

Ответ 5

XML Notepad 2007 может сделать это вручную... дайте мне посмотреть, можно ли его сценаризировать.

Нет... он может запускать его так:

XmlNotepad.exe a.xml

Остальное просто нажимает кнопку сохранения. Power Shell, другие инструменты могут автоматизировать это.

Ответ 6

Просто используйте этот xslt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" encoding="ISO-8859-1"/>
  <xsl:param name="indent-increment" select="'   '"/>

  <xsl:template name="newline">
    <xsl:text disable-output-escaping="yes">
</xsl:text>
  </xsl:template>

  <xsl:template match="comment() | processing-instruction()">
    <xsl:param name="indent" select="''"/>
    <xsl:call-template name="newline"/>    
    <xsl:value-of select="$indent"/>
    <xsl:copy />
  </xsl:template>

  <xsl:template match="text()">
    <xsl:param name="indent" select="''"/>
    <xsl:call-template name="newline"/>    
    <xsl:value-of select="$indent"/>
    <xsl:value-of select="normalize-space(.)"/>
  </xsl:template>

  <xsl:template match="text()[normalize-space(.)='']"/>

  <xsl:template match="*">
    <xsl:param name="indent" select="''"/>
    <xsl:call-template name="newline"/>    
    <xsl:value-of select="$indent"/>
      <xsl:choose>
       <xsl:when test="count(child::*) > 0">
        <xsl:copy>
         <xsl:copy-of select="@*"/>
         <xsl:apply-templates select="*|text()">
           <xsl:with-param name="indent" select="concat ($indent, $indent-increment)"/>
         </xsl:apply-templates>
         <xsl:call-template name="newline"/>
         <xsl:value-of select="$indent"/>
        </xsl:copy>
       </xsl:when>       
       <xsl:otherwise>
        <xsl:copy-of select="."/>
       </xsl:otherwise>
     </xsl:choose>
  </xsl:template>    
</xsl:stylesheet>

Или, как еще один вариант, здесь находится perl script: http://software.decisionsoft.com/index.html

Ответ 7

Вы можете реализовать простое приложение SAX, которое будет копировать все as is и атрибуты отступа, как вам нравится.

UPD:

SAX означает Simple API for XML. Это модель моделирования XML-анализа (классический пример шаблона проектирования Builder). API присутствует на большинстве современных платформ разработки (хотя в родной библиотеке классов.Net не хватает одного, имея XMLReader)

Вот сырая реализация в python, она довольно загадочная, но вы можете реализовать основную идею.

from sys import stdout
from xml.sax import parse
from xml.sax.handler import ContentHandler
from xml.sax.saxutils import escape

class MyHandler(ContentHandler):

    def __init__(self, file_, encoding):
        self.level = 0
        self.elem_indent = '    '

        # should the next block make a line break
        self._allow_N = False
        # whether the opening tag was closed with > (to allow />)
        self._tag_open = False

        self._file = file_
        self._encoding = encoding

    def _write(self, string_):
        self._file.write(string_.encode(self._encoding))

    def startElement(self, name, attrs):
        if self._tag_open:
            self._write('>')
            self._tag_open = False

        if self._allow_N:
            self._write('\n')
            indent = self.elem_indent * self.level
        else:
            indent = ''
        self._write('%s<%s' % (indent, name))

        # attr indent equals to the element indent plus '  '
        attr_indent = self.elem_indent * self.level + '  '
        for name in attrs.getNames():
            # write indented attribute one per line
            self._write('\n%s%s="%s"' % (attr_indent, name, escape(attrs.getValue(name))))

        self._tag_open = True

        self.level += 1
        self._allow_N = True

    def endElement(self, name):
        self.level -= 1
        if self._tag_open:
            self._write(' />')
            self._tag_open = False
            return

        if self._allow_N:
            self._write('\n')
            indent = self.elem_indent * self.level
        else:
            indent = ''
        self._write('%s</%s>' % (indent, name))
        self._allow_N = True

    def characters(self, content):
        if self._tag_open:
            self._write('>')
            self._tag_open = False

        if content.strip():
            self._allow_N = False
            self._write(escape(content))
        else:
            self._allow_N = True


if __name__ == '__main__':
    parser = parse('test.xsl', MyHandler(stdout, stdout.encoding))