Skip to content

Instantly share code, notes, and snippets.

@rplantiko
Created July 10, 2015 10:37
Show Gist options
  • Save rplantiko/b92f0231c4e149bf75aa to your computer and use it in GitHub Desktop.
Save rplantiko/b92f0231c4e149bf75aa to your computer and use it in GitHub Desktop.
XML beautifier with attribute alignment
/* global process */
// A simple xml beautifier, based on the xml2js parser
// - align attribute names (this is what xmlbuilder can't do)
// - indent by 2 spaces
// Don't expect too much:
// xml2js can't handle mixed element content:
// <a>b<c/>d</a> is equal to <a>bd<c/></a> in the eyes of xml2js
(function(){
var fs = require('fs')
var xml2js = require('xml2js')
if (process.argv.length < 3) {
process.stderr.write("Please specify source xml file name")
process.exit(1)
}
fs.readFile( process.argv[2], 'utf8', onSuccess( function(data) {
var parser = new xml2js.Parser()
parser.parseString(data, onSuccess( function(xjson) {
var lines = beautify(xjson)
process.stdout.write(lines.join('\n'))
}))
}))
function onSuccess(f) {
return function(err,data) {
if (err) console.dir(err)
else f(data)
}
}
function beautify(xjson) {
var indent = ""
var lines = []
traverse(xjson)
return lines
function traverse(node, name ) {
if (name === undefined) return handleRoot()
if (typeof node == "string") return handleTextContent()
if (node instanceof Array) return handleArray()
handleOpenElement()
handleElementContent()
handleCloseElement()
function handleRoot() {
console.assert(typeof node == "object", "Call beautify with a JSON object")
var keys = Object.keys(node)
console.assert(keys.length == 1, "An XML object must have exactly 1 root element")
traverse( node[keys[0]], keys[0] )
}
function handleOpenElement() {
var line = indent + "<" + name
if (node.hasOwnProperty("$")) handleAttributes()
line += ">"
lines.push(line)
indent += " "
function handleAttributes() {
line += " "
var attindent = line.replace(/./g," ")
var numatts = Object.keys(node.$).length
Object.keys(node.$).forEach( function(attname,i) {
var att = attname + '="' + node.$[attname].replace(/"/g,'&#34') + '"'
line += att
if (i==numatts-1) return
lines.push(line)
line = attindent
})
}
}
function handleElementContent() {
Object.keys(node)
.filter( function(key) { return key!='$' })
.forEach( function(key) {
var y = node[key]
traverse(y,key)
})
}
function handleTextContent() {
if (node) lines.push( indent + node )
}
function handleCloseElement() {
indent = indent.slice(0,-2)
lines.push( indent + "</" + name + ">" )
}
function handleArray() {
// empty element
if ( (node.length == 1) && (node[0] === '')) {
lines.push(indent+'<'+name+'/>')
}
// xml2js way of denoting successive elements of the same element name
node.forEach(
function(item) { traverse(item,name) }
)
}
}
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment