xml prettifier Topic is solved

Get help with using AutoHotkey and its commands and hotkeys
Guest

xml prettifier

15 Nov 2013, 10:05

Found this function somewhere, but it has a bug for the first line and comments, it places the first tag right after the header.
can someone look at it please? Test xml code included

[code=autohotkey file=Script.ahk]#NoEnv
#SingleInstance force

xml_content=
(
<?xml version="1.0" encoding="UTF-8"?>
<!--
-comment
-->
<root>
<books>
<book_1>book 1</book_1>
<book_2>book 2</book_2>
</books>
<animals>
<book_1>birds</book_1>
<book_2>fish</book_2>
</animals>
</root>
)

xml_content_2=
(
<?xml version="1.0" encoding="UTF-8"?>
<root>
<books>
<book_1>book 1</book_1>
<book_2>book 2</book_2>
</books>
<animals>
<book_1>birds</book_1>
<book_2>fish</book_2>
</animals>
</root>
)

msgBox, % xml_prettifier(xml_content)
msgBox, % xml_prettifier(xml_content_2)
ExitApp

xml_prettifier(XML, Tab="`t"){
oel := ErrorLevel, PrevCloseTag := 0, tabs := "", tablen := StrLen( tab )
StringLen, pos, XML
Loop, Parse, XML, <, % "`t`r`n "
If ( A_Index = 1 )
VarSetCapacity( XML, pos, 0 )
Else
{
StringGetPos, pos, A_LoopField, >
StringMid, b, A_LoopField, pos, 1
StringLeft, a, A_LoopField, 1
If !( OpenTag := a != "/" ) * ( CloseTag := a = "/" || a = "!" || a = "?" || b = "/" )
StringTrimRight, tabs, tabs, tablen
XML .= ( OpenTag || PrevCloseTag ? tabs : "" ) "<" A_LoopField
If !( PrevCloseTag := CloseTag ) * OpenTag
tabs := ( tabs = "" ? "`n" : tabs ) tab
}
Return XML, ErrorLevel := oel
}[/code]
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: xml prettifier  Topic is solved

15 Nov 2013, 11:37

Here's an alternative pretty-printer:

Code: Select all

xml_content =
(LTrim Join
<?xml version="1.0" encoding="UTF-8"?>
<!--
-comment
-->
<root>
<books>
<book_1>book 1</book_1>
<book_2>book 2</book_2>
</books>
<animals>
<book_1>birds</book_1>
<book_2>fish</book_2>
</animals>
</root>
)
MsgBox, % "BEFORE:`n`n" . xml_content
MsgBox, % "AFTER:`n`n" . xml_prettify(xml_content, "    ") ; specify any char to use as indentation
return



xml_prettify(xml_source, indent:="`t") {
	static xsl, style
	
	if !xsl {
		style := "
		(LTrim Join
		<?xml version='1.0' encoding='ISO-8859-15'?>
		<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
		<xsl:output method='xml'/>

		<xsl:template match='@*'>
		<xsl:copy/>
		</xsl:template>

		<xsl:template match='text()'>
		<xsl:value-of select='normalize-space(.)' />
		</xsl:template>

		<xsl:template match='*'>
		<xsl:param name='indent' select='""""'/>
		<xsl:text>&#xa;</xsl:text>
		<xsl:value-of select='$indent' />
		<xsl:copy>
		<!--<xsl:apply-templates select='@*|*|text()'>-->
		<xsl:apply-templates select='@*|node()'>
		<xsl:with-param name='indent' select='concat($indent, ""  "")'/>
		</xsl:apply-templates>
		</xsl:copy>
		<xsl:if test='count(../*)>0 and ../*[last()]=.'>
		<xsl:text>&#xa;</xsl:text>
		<xsl:value-of select='substring($indent,3)' />
		</xsl:if>
		</xsl:template>

		</xsl:stylesheet>
		)"
		xsl := ComObjCreate("MSXML2.DOMDocument.6.0")
		xsl.async := false
		xsl.loadXML(style)
	}

	a := xsl.selectSingleNode("//*[local-name()='with-param'][@name='indent']/@select")
	a.value := "concat($indent, """ . indent  . """)"

	b := xsl.selectSingleNode("//*[local-name()='if']/*[local-name()='value-of']/@select")
	b.value := "substring($indent," . StrLen(indent)+1 . ")"

	xml := ComObjCreate("MSXML2.DOMDocument.6.0")
	xml.async := false
	xml.loadXML(xml_source)

	pretty_xml := xml.transformNode(xsl)
	;xml.transformNodeToObject(xsl, xml) ; does not work if indent char is whitespace??
	;pretty_xml := xml.xml
	return pretty_xml
}
Guest

Re: xml prettifier

15 Nov 2013, 13:12

Thanks for trying to help, but your solution is not really what i want, its stripping comments, spaces and probably tabs off whats in between the tags :/
I just need it to make it pretty.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: xml prettifier

15 Nov 2013, 14:28

Try this: (not tested extensively)

Code: Select all

src_1 =
(Join
<?xml version="1.0" encoding="UTF-8"?>
<!--
-comment
-->
<root>
<books>
<book_1>book 1</book_1>
<book_2>book 2</book_2>
</books>
<animals>
<book_1>birds</book_1>
<book_2>fish</book_2>
</animals>
</root>
)

src_2 =
(
<?xml version="1.0" encoding="UTF-8"?>
<root>
<books>
<book_1>book 1</book_1>
<book_2>book 2</book_2>
</books>
<animals>
<book_1>birds</book_1>
<book_2>fish</book_2>
</animals>
</root>
)

MsgBox, % xml_prettify(src_1, "    ")
MsgBox, % xml_prettify(src_2, "`t")

xml_prettify(node, i:="    ", lvl:=0) {
	if !IsObject(node) {
		x := ComObjCreate("MSXML2.DOMDocument.6.0")
		x.async := false, x.loadXML(node)
		return xml_prettify(x)
	}
	
	start := end := ""
	if node.hasChildNodes() {
		n := "`n"
		Loop, % lvl
			t .= i
		
		if (node.nodeTypeString == "element") {
			start := SubStr(node.xml, 1, InStr(node.xml, ">"))
			end := SubStr(node.xml, InStr(node.xml, "<",, 0))
		}

		for c in node.childNodes {
			try val := c.selectNodes("child::node()[not(self::text())]").length
			           ? xml_prettify(c, i, lvl+1)
			           : c.xml
			catch
				val := c.xml
			str .= val . n . t
		}
		str := (lvl ? n . t : "") . Trim(str, ",`n`t ") . n . SubStr(t, StrLen(i)+1)
	
	} else str := node.xml

	return start . str . end
}
User avatar
VxE
Posts: 44
Joined: 30 Sep 2013, 10:35
Location: Simi Valley, CA

Re: xml prettifier

15 Nov 2013, 20:03

Yeah, that looks like my code. Here's the updated function:

Code: Select all

sXML_Pretty( XML, IndentationUnit="`t" ) { ; Adds linefeeds (LF, asc 10) and indentation between XML tags.
; NOTE: If the XML does not begin with a "<?xml...?>" tag, the output will begin with a newline.
	StringLen, il, IndentationUnit
	Loop, Parse, XML, <
		If ( A_Index = 1 )
			VarSetCapacity( XML, Round( StrLen( XML ) * ( A_IsUnicode ? 2.3 : 1.15 ) ), 0 )
		Else
			Loop, Parse, A_LoopField, >, % "`t`n`r " Chr( 160 )
				If ( A_Index = 1 )
				{
					closetag := SubStr( A_LoopField, 1, 1 )
					emptytag := SubStr( A_LoopField, 0 )
					If ( closetag = "?" ) && ( emptytag = "?" )
						XML := "<" A_LoopField
					Else
					{
						If ( closetag = "/" )
						{
							StringTrimRight, indent, indent, il
							If ( priortag )
								XML .= "`n" indent
						}
						Else
						{
							XML .= "`n" indent
							If ( emptytag != "/" ) && ( closetag != "!" )
								indent .= IndentationUnit
						}
						XML .= "<" A_LoopField
					}
					priortag := closetag = "/" || closetag = "!" || emptytag = "/"
				}
				Else
					XML .= ">" A_LoopField
	Return XML
} ; END - sXML_Pretty( XML, IndentationUnit="`t" )

Return to “Ask For Help”

Who is online

Users browsing this forum: AHKStudent, Bing [Bot], bracht, kunkel321, mikeyww and 38 guests