以编程方式格式化XML,使其呈缩进形式,就像Visual Studio的自动格式化一样。

6

我还没有找到使用.NET的XmlWriter和相关的XmlWriterSettings以与Visual Studio自动格式化命令(Ctrl-E Ctrl-D,或者根据键盘映射,Ctrl-K Ctrl-D)完全相同的缩进形式格式化XML字符串的方法。

我想这样做是因为我习惯在VS中自动格式化所有文件,包括代码和.config文件。我有一个安装程序应用程序,可以更新.config文件,并且我想看到实际的差异,而不是整个文档被更改。

我还没有探索所有自动格式化的不同选项,但我喜欢每个XML属性都在单独的一行上,第一个属性与开放标签在同一行上,后续属性与第一个属性对齐,就像这样:

<asset assetId="12345"
       bucket="default"
       owner="nobody">
  <file path="\\localhost\share\assetA.mov"/>
  <metadata metadataId="23456"
            key="asset_type"
            value="video"/>
</asset>

我尝试使用XmlWriterSettings属性“NewLineHandling = NewLineHandling.None”和“NewLineOnAttributes = true”进行格式化,但这会将第一个属性放在开标签下方,并且所有属性的缩进都相同,无论元素名称中有多少个字符,就像这样:

<asset
  assetId="12345"
  bucket="default"
  owner="nobody">
  <file
    path="\\localhost\share\assetA.mov" />
  <metadata metadataId="23456"
    key="asset_type"
    value="video" />
</asset>

注意标准的XmlWriter也会在仅具有属性的元素后加上" />"(斜杠前多一个空格),这是我不喜欢的,但我不确定是否符合XML标准。我认为Visual Studio应该使用相同的API选项供开发人员方便使用,但是我还没有找到那些神奇的设置。无论如何,下面是我的格式化方法:

public static string FormatXml( string xmlString, bool indented )
{
    using ( TextReader textReader = new StringReader( xmlString ) )
    using ( XmlReader xmlReader = new XmlTextReader( textReader ) )
    {
        using ( TextWriter textWriter = new StringWriter() )
        {
            var settings = new XmlWriterSettings();
            if ( indented )
            {
               settings.Indent = true;
               settings.IndentChars = "  ";
               settings.NewLineOnAttributes = true;
               settings.NewLineHandling = NewLineHandling.None;
            }
            using ( var xmlWriter = XmlWriter.Create( textWriter, settings ) )
            {
                while ( xmlReader.Read() )
                    xmlWriter.WriteNode( xmlReader, false );
            }
            return textWriter.ToString();
        }
    }
}
2个回答

1

我误解了问题。实际上,我不知道是否有一种方法可以按照您展示的方式对齐属性。您可以尝试自己实现,类似于这样:

    public static string FormatXml(string xmlString, bool indented)
    {
        using (TextReader textReader = new StringReader(xmlString))
        using (XmlReader xmlReader = new XmlTextReader(textReader))
        {
            using (TextWriter textWriter = new StringWriter())
            {
                string indent = "";
                string attributeIndent = "";

                while (xmlReader.Read())
                {
                    if (xmlReader.NodeType == XmlNodeType.Element)
                    {
                        attributeIndent = "";
                        string element = xmlReader.Name;
                        textWriter.Write("{0}<{1}", indent, element);

                        if (!xmlReader.HasAttributes)
                            textWriter.WriteLine(">");
                        else
                        {
                            int actual = 1;
                            while (xmlReader.MoveToNextAttribute())
                            {
                                string content = String.Format("{0} {1}={2}", attributeIndent, xmlReader.Name, xmlReader.Value);
                                if (actual != xmlReader.AttributeCount)
                                    textWriter.WriteLine(content);
                                else
                                    textWriter.Write(content);

                                if (string.IsNullOrEmpty(attributeIndent))
                                    attributeIndent = indent + Enumerable.Repeat<string>(" ", element.Length + 1).Aggregate((a, b) => a + b);

                                actual++;
                            }
                            xmlReader.MoveToElement();
                            textWriter.WriteLine(">");
                            attributeIndent = "";
                        }
                        if (!xmlReader.IsEmptyElement)
                        {
                            indent += "  ";
                            textWriter.WriteLine(">");
                        }
                        else
                            textWriter.WriteLine("/>");
                    }
                    else if (xmlReader.NodeType == XmlNodeType.EndElement)
                    {
                        indent = indent.Substring(0, indent.Length - 2);
                        textWriter.WriteLine("{0}</{1}>", indent, xmlReader.Name);
                    }
                    else if (xmlReader.NodeType == XmlNodeType.Text)
                    {
                        textWriter.WriteLine(xmlReader.Value);
                    }
                }

                return textWriter.ToString();
            }
        }
    }

谢谢提供这个例子。我还没有尝试过,但是我知道我可以手动输出元素并处理不同的节点类型,但我希望微软不会隐藏他们的Visual Studio实现。 - Erhhung
其实我不知道是否有相关内容;我在谷歌上搜了一下,但没有找到。希望我发布的内容能对你有所帮助。 - as-cii

0

我认为引号是无效的xml,这可能会让读者/写作者感到不适。除此之外,你为什么不告诉你的diff应用程序忽略空格呢?


在大多数 diff 应用程序中,“忽略空格”不会跨越多行。 - Tim Sparkles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接