Skip to content

Instantly share code, notes, and snippets.

@mavaddat
Last active March 29, 2023 13:00
Show Gist options
  • Save mavaddat/a82cc0328a6b6d80dfbb927e0ac57bf2 to your computer and use it in GitHub Desktop.
Save mavaddat/a82cc0328a6b6d80dfbb927e0ac57bf2 to your computer and use it in GitHub Desktop.
Close Elements SGML to XML
function Close-Elements
{
[CmdletBinding()]
param(
[Parameter(Mandatory, ParameterSetName="ReaderGiven", ValueFromPipeline, Position = 0)]
[ValidateNotNullOrEmpty()]
[System.Xml.XmlReader] $Reader,
[Parameter(Mandatory, ParameterSetName="InPathGiven", ValueFromPipeline, Position = 0)]
[ValidateNotNullOrEmpty()]
[string] $InPath,
[Parameter(Mandatory, ParameterSetName="WriterGiven", Position = 1)]
[Parameter(ParameterSetName="InPathGiven", Position = 1)]
[ValidateNotNullOrEmpty()]
[System.Xml.XmlWriter] $Writer,
[Parameter(Mandatory, ParameterSetName="OutPathGiven", Position = 1)]
[Parameter(ParameterSetName="InPathGiven", Position = 1)]
[ValidateNotNullOrEmpty()]
[string] $OutPath
)
<# Thanks to Scott Hanselman http://www.hanselman.com/blog/postprocessing-autoclosed-sgml-tags-with-the-sgmlreader #>
# $msgBody = $Reader.NameTable.Add('MSGBODY')
if($PSCmdlet.ParameterSetName -eq "OutPathGiven")
{
$WriterSettings = [System.Xml.XmlWriterSettings]::new()
$WriterSettings.Indent = $true
$WriterSettings.IndentChars = ' '*4
$WriterSettings.ConformanceLevel = [System.Xml.ConformanceLevel]::Fragment
$Writer = [System.Xml.XmlWriter]::Create($OutPath, $WriterSettings)
}
if($PSCmdlet.ParameterSetName -eq "InPathGiven"){
$Reader = [Sgml.SgmlReader]::Create($InPath)
}
$selfClosingTags = 'recbomba', 'recinser', 'trbomba', 'trinser', 'spanspec', 'colspec', 'mtoc', 'ptitle', 'pnote', 'usercomm', 'xmit', 'ctoc', 'sbeff', 'unitid', 'mtoc', 'mlep', 'clep', 'itemcol', 'mfmatr', 'sbtitle', 'sbdata', 'sbinfo', 'sblist'
foreach ($tag in $selfClosingTags) {
$Reader.NameTable.Add($tag)
}
$previousElement = $null
$elementsWeAlreadyEnded = New-Object System.Collections.Stack
while ($Reader.Read())
{
switch ($Reader.NodeType)
{
XmlNodeType.Element
{
$previousElement = $Reader.LocalName
$Writer.WriteStartElement($Reader.LocalName)
break
}
XmlNodeType.Text
{
if (-not [string]::IsNullOrEmpty($Reader.Value))
{
$Writer.WriteString($Reader.Value.Trim())
if ($null -ne $previousElement -and $previousElement -notin $selfClosingTags)
{
$Writer.WriteEndElement()
$elementsWeAlreadyEnded.Push($previousElement)
}
}
else
{
Write-Debug -Message "Empty text node"
}
break
}
XmlNodeType.EndElement
{
if ($elementsWeAlreadyEnded.Count -gt 0 -and $elementsWeAlreadyEnded.Peek() -eq $Reader.LocalName)
{
# Next in the stack is the current end element, so pop it off
$elementsWeAlreadyEnded.Pop() | Out-Null
}
else
{
$Writer.WriteEndElement()
}
break
}
default
{
$Writer.WriteNode($Reader, $false)
break
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment