Есть ли таблица стилей или инструмент командной строки Windows для контролируемого форматирования XML, в частности, размещение атрибутов по одной на строку?
Я ищу XSLT или инструмент командной строки (или код C#, который можно превратить в инструмент командной строки и т. Д.) Для Windows, который будет выполнять красивую печать XML. В частности, я хочу тот, который имеет возможность помещать атрибуты в строку, что-то вроде:
<Node>
<ChildNode
value1='5'
value2='6'
value3='happy' />
</Node>
Это не обязательно должно быть ТОЧНО, но я хочу использовать его для XML-файла, в котором есть узлы с десятками атрибутов, а распределение по нескольким строкам упрощает их чтение, редактирование и разность текста.
ПРИМЕЧАНИЕ. Я думаю, что моим предпочтительным решением является лист XSLT, который я могу пройти с помощью метода C#, хотя инструмент командной строки Windows также хорош.
7 ответов
Вот небольшой пример C#, который может быть использован непосредственно вашим кодом или встроен в exe и вызван в командной строке как "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();
Вот сценарий PowerShell, чтобы сделать это. Требуется следующий вход:
<?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))
Возьмите этот скрипт, сохраните его как C: \ formatxml.ps1. Затем из командной строки PowerShell введите следующее:
C:\formatxml.ps1 C:\Path\To\UglyFile.xml C:\Path\To\NeatAndTidyFile.xml
Этот скрипт в основном использует.NET Framework, поэтому вы можете легко перенести его в приложение C#.
ПРИМЕЧАНИЕ. Если вы ранее не запускали сценарии из PowerShell, вам потребуется выполнить следующую команду в командной строке PowerShell с повышенными правами, прежде чем вы сможете выполнить сценарий:
Set-ExecutionPolicy RemoteSigned
Вы должны сделать это только один раз, хотя.
Я надеюсь, что это полезно для вас.
Попробуйте Tidy на SourceForge. Хотя он часто используется в [X]HTML, я уже успешно использовал его в XML - просто убедитесь, что вы используете -xml
вариант.
http://tidy.sourceforge.net/docs/tidy_man.html
Tidy читает файлы HTML, XHTML и XML и пишет очищенную разметку.... Для общих XML-файлов Tidy ограничивается исправлением основных ошибок правильной формы и красивой печатью.
Люди перенесли на несколько платформ, и он доступен как исполняемая и вызываемая библиотека.
У Tidy есть куча вариантов, в том числе:
http://tidy.sourceforge.net/docs/quickref.html
отступ-атрибуты
Тип верха: логическое значение
По умолчанию: нет Пример: y / n, да / нет, t/f, true/false, 1/0
Эта опция указывает, должен ли Tidy начинать каждый атрибут с новой строки.
Одно предостережение:
Ограниченная поддержка XML
Процессоры XML, совместимые с рекомендацией W3C XML 1.0, очень требовательны к тому, какие файлы они будут принимать. Tidy может помочь вам исправить ошибки, которые приводят к отклонению ваших файлов XML. Tidy еще не распознает все возможности XML, например, он не понимает разделы CDATA или подмножества DTD.
Но я подозреваю, что если ваш XML не является действительно продвинутым, инструмент должен работать нормально.
Существует инструмент, который может разделять атрибуты по одному на строку: xmlpp. Это скрипт на Perl, поэтому вам придется установить Perl. Использование:
perl xmlpp.pl -t input.xml
Вы также можете определить порядок атрибутов, создав файл attributeOrdering.txt и вызвав perl xmlpp.pl -s -t input.xml
, Для большего количества вариантов используйте perl xmlpp.pl -h
Надеюсь, в ней не так много ошибок, но до сих пор это сработало.
Вы можете реализовать простое приложение SAX, которое будет копировать все as is
и отступ атрибутов, как вам нравится.
UPD:
SAX обозначает Simple API for XML
, Это push-модель парсинга 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))
XML Notepad 2007 может сделать это вручную... давайте посмотрим, можно ли его написать в сценарии.
Нет... он может запустить его так:
XmlNotepad.exe a.xml
Остальное просто нажмите кнопку Сохранить. Power Shell, другие инструменты могут автоматизировать это.
Просто используйте этот 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: http://software.decisionsoft.com/index.html