首页 > 解决方案 > 如何将 XmlDocument 保存到“格式类似于 Visual Studio”的 .csproj 中,其中空元素保存在单独的行中?

问题描述

目前,我创建了一个 PowerShell 脚本来检查数百个(是的,数百个)Visual Studio 项目并更新参考以保持一致性,从而修复了许多细微的错误。这适用于能够往返99% 的 XML,以便后续在 Visual Studio 中编辑项目文件不会引入其他更改。

但是,使用XmlDocument.Save(), EMPTY 元素作为一对保存在同一行..

<StartupObject></StartupObject>

..而不是 Visual Studio 项目编辑器将 EMPTY 元素保存在两行之间的方式..

<StartupObject>
</StartupObject>

这会导致初始提交产生不必要的噪音,当程序员从 Visual Studio 中更新项目时,这些噪音将被重置。

如何XmlDocument使用 Visual Studio [至少截至 2017 年] 使用的相同格式规则保存 [修改]?

目前,保存是使用显式XmlWriterXmlWriterSettings使用以下配置完成的:

$writerSettings = New-Object System.Xml.XmlWriterSettings
$writerSettings.Indent = $true
$writerSettings.IndentChars = $indent # value stolen previously

$writer = [System.Xml.XmlWriter]::Create($path, $writerSettings)
$xml.Save($writer)

如果使用该XmlDocument.PreserveWhitespace设置,未修改的节点不受影响。但是,在这种情况下,“格式不正确”的条目不是固定的,并且新节点没有应用正确的缩进/格式。

优选地,这可以通过对保存和/或设置的简单修改而不是自定义编写器(脚本在 PowerShell 中)或一些保存后文本操作(因此感觉有点麻烦)来处理。

标签: .netxmlpowershellformattingvisual-studio-project

解决方案


虽然总的来说我认为你不应该使用正则表达式来篡改 XML,但我也找不到 XmlWriter 类中的任何设置会改变写入空元素的样式。

我将其格式化为您想要的方式的唯一方法是让 XmlWriter 写入内存流并-replace在其上使用正则表达式:

$writerSettings             = New-Object System.Xml.XmlWriterSettings
$writerSettings.Indent      = $true
$writerSettings.IndentChars = '  '
$writerSettings.Encoding    = New-Object System.Text.UTF8Encoding $false  # set to UTF-8 No BOM

# create a stream object to write to
$stream = New-Object System.IO.MemoryStream

$writer = [System.Xml.XmlWriter]::Create($stream, $writerSettings)
$writer.WriteStartDocument()
$writer.WriteStartElement("Root")
$writer.WriteElementString("StartupObject", [string]::Empty)
$writer.WriteElementString("AnotherEmptyElement", [string]::Empty)
$writer.WriteEndElement()
$writer.WriteEndDocument()
$writer.Flush()
$writer.Dispose()

# get whatever is written to the stream in a string variable
$xml = [System.Text.Encoding]::Default.GetString(($stream.ToArray()))

# replace self-closing elements: <StartupObject />
# replace empty elements in one line: <StartupObject></StartupObject>
# to the format where opening and closing tags are on separate lines
$format = '$1<$2>{0}$1</$2>' -f [Environment]::NewLine
$xml = $xml -replace '(.*?)<(.*?)\s*/>', $format -replace '(.*?)<(.*?)></(.*?)>', $format

# save do disk
$xml | Out-File -FilePath 'D:\test.xml' -Force -Encoding default

结果:

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <StartupObject>
  </StartupObject>
  <AnotherEmptyElement>
  </AnotherEmptyElement>
</Root>

推荐阅读