-
-
Save sente/1083506 to your computer and use it in GitHub Desktop.
The MIT License (MIT) | |
Copyright (c) 2016 Stuart Powers | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
function formatXml(xml) { | |
var formatted = ''; | |
var reg = /(>)(<)(\/*)/g; | |
xml = xml.replace(reg, '$1\r\n$2$3'); | |
var pad = 0; | |
jQuery.each(xml.split('\r\n'), function(index, node) { | |
var indent = 0; | |
if (node.match( /.+<\/\w[^>]*>$/ )) { | |
indent = 0; | |
} else if (node.match( /^<\/\w/ )) { | |
if (pad != 0) { | |
pad -= 1; | |
} | |
} else if (node.match( /^<\w([^>]*[^\/])?>.*$/ )) { | |
indent = 1; | |
} else { | |
indent = 0; | |
} | |
var padding = ''; | |
for (var i = 0; i < pad; i++) { | |
padding += ' '; | |
} | |
formatted += padding + node + '\r\n'; | |
pad += indent; | |
}); | |
return formatted; | |
} | |
xml_raw = '<foo><bar><baz>blahblah</baz><baz>tralala</baz></bar></foo>'; | |
xml_formatted = formatXml(xml_raw); | |
xml_escaped = xml_formatted.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/ /g, ' ').replace(/\n/g,'<br />'); | |
var mydiv = document.createElement('div'); | |
mydiv.innerHTML = xml_escaped; | |
document.body.appendChild(mydiv); | |
<html> | |
<head> | |
<title>Javascript xml pretty print - Stuart Powers</title> | |
<script src="http://bit.ly/jqymin"></script> | |
<style> | |
textarea { | |
width: 80%; | |
height: 50%; | |
overflow-x: scroll; | |
overflow-y: scroll; | |
display: block; | |
border: 1px solid black; | |
padding: 5px; | |
margin: 5px; | |
} | |
</style> | |
<script> | |
function formatXml(xml) { | |
var formatted = ''; | |
var reg = /(>)(<)(\/*)/g; | |
xml = xml.replace(reg, '$1\r\n$2$3'); | |
var pad = 0; | |
jQuery.each(xml.split('\r\n'), function(index, node) | |
{ | |
var indent = 0; | |
if (node.match( /.+<\/\w[^>]*>$/ )) | |
{ | |
indent = 0; | |
} | |
else if (node.match( /^<\/\w/ )) | |
{ | |
if (pad != 0) | |
{ | |
pad -= 1; | |
} | |
} | |
else if (node.match( /^<\w[^>]*[^\/]>.*$/ )) | |
{ | |
indent = 1; | |
} | |
else | |
{ | |
indent = 0; | |
} | |
var padding = ''; | |
for (var i = 0; i < pad; i++) | |
{ | |
padding += ' '; | |
} | |
formatted += padding + node + '\r\n'; | |
pad += indent; | |
}); | |
return formatted; | |
} | |
$(function(){ | |
var url = "http://careers.stackoverflow.com/jobs/feed?searchTerm=python&location=02144&range=30"; | |
$.ajax({ | |
url: url, | |
dataType:"text", | |
beforeSend:function(){$('.info').append($('<p>requesting: '+url+'</p>'));}, | |
error:function(){$('.info').append($('<p>error! '+url+'</p>'));}, | |
success: function(data) { | |
$('.unformatted').text(data); | |
} | |
}); | |
$.ajax({ | |
url: url, | |
dataType:"text", | |
beforeSend:function(){ | |
$('.info2').append($('<p>requesting '+url+'</p>')); | |
$('.info2').append($('<p>and formatting the response!')); | |
}, | |
error:function(){$('.info2').append($('<p>error! '+url+'</p>'));}, | |
success: function(data) { | |
xml_neat = formatXml(data); | |
$('.formatted').text(xml_neat); | |
} | |
}); | |
}); | |
</script> | |
</head> | |
<body> | |
<code> | |
Also available <a href="http://sente.cc/misc/javascript_xml_pretty_print.html"> | |
http://sente.cc/misc/javascript_xml_pretty_print.html | |
</a> | |
</code> | |
<pre class="info"></pre> | |
<h3>unformatted</h3> | |
<textarea class="unformatted"></textarea> | |
<h3>formatted</h3> | |
<textarea class="formatted"></textarea> | |
<body> | |
</html> |
Thank you. Works perfectly!
node.match(/^<\w[^>][^\/]>.$/) seems not to match single letter elements like
Thank u so much .. It helps a lot
@Risord Indeed, that regular expression should be /^<\w([^>]*[^\/])?>.*$/
Thanks for the code.
Here's an adapted ES6 variant:
formatXml(xml) {
const PADDING = ' '.repeat(2); // set desired indent size here
const reg = /(>)(<)(\/*)/g;
let pad = 0;
xml = xml.replace(reg, '$1\r\n$2$3');
return xml.split('\r\n').map((node, index) => {
let indent = 0;
if (node.match(/.+<\/\w[^>]*>$/)) {
indent = 0;
} else if (node.match(/^<\/\w/) && pad > 0) {
pad -= 1;
} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
indent = 1;
} else {
indent = 0;
}
pad += indent;
return PADDING.repeat(pad - indent) + node;
}).join('\r\n');
}
ES6 variant works for me. Thanks!
@grubersjoe
js lint fails with this message:
Unnecessary escape character: \/ no-useless-escape
so I used [^/]
in last node.match
function
Many Thanks,, works perfect!!
did you try script on bad formed xml like
<feed xmlns="http://www.w3.org/2005/Atom"
need="urgent"> <title type="general" sub="sample">Example Feed</title>
<subtitle><![CDATA[minor subtitle <<< ]]></subtitle>
<author nn="1" order="2" case="now"><name>John Doe</name><email>johndoe@example.com</email>
</author> <entry><title>Atom-Powered Robots Run Amok</title>
<link href="http://example.org/2003/12/13/atom03" />
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated><summary>Some text.</summary>
</entry> </feed>
? it does not work for me
@grubersjoe
Many thanks!
@ sente
Wow, so cool, thanks!
<simple-value><![CDATA[部门
Dept]]>
after format xml, it will be follow result, have many space char before <![CDATA
i had try, modify ,
var reg = /(>)(<)([^!])(/*)/g;
thanks \o
Good work - although I must point out that it doesn't touch existing whitespace indentation, with hilarious consequences.
Thanks for the code.
Here's an adapted ES6 variant:
formatXml(xml) { const PADDING = ' '.repeat(2); // set desired indent size here const reg = /(>)(<)(\/*)/g; let pad = 0; xml = xml.replace(reg, '$1\r\n$2$3'); return xml.split('\r\n').map((node, index) => { let indent = 0; if (node.match(/.+<\/\w[^>]*>$/)) { indent = 0; } else if (node.match(/^<\/\w/) && pad > 0) { pad -= 1; } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { indent = 1; } else { indent = 0; } pad += indent; return PADDING.repeat(pad - indent) + node; }).join('\r\n'); }
Thanks for the ES6 version @grubersjoe ,a small addition though for handling newlines and existing whitespace between tags(as pointed out by @brennanyoung):
formatXml(xml) {
// Remove all the newlines and then remove all the spaces between tags
xml = xml.replace(/(\r\n|\n|\r)/gm, " ").replace(/>\s+</g,'><');
const PADDING = ' '.repeat(4); // set desired indent size here
const reg = /(>)(<)(\/*)/g;
let pad = 0;
xml = xml.replace(reg, '$1\r\n$2$3');
return xml.split('\r\n').map((node, index) => {
let indent = 0;
if (node.match(/.+<\/\w[^>]*>$/)) {
indent = 0;
} else if (node.match(/^<\/\w/) && pad > 0) {
pad -= 1;
} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
indent = 1;
} else {
indent = 0;
}
pad += indent;
return PADDING.repeat(pad - indent) + node;
}).join('\r\n');
}
I'm not sure if the latest versions of FF added more constraints, I'm getting "Toot much recursion" from formatXml()
A slight mismatch in one of the regular expressions needs to be: node.match(/^<[\w^>]*[^\/]>.*$/))
. Merging everyone's updates, here's what I've been using:
/**
* Prettifies the tab indentation of given XML string
*
* @param xml string - The XML to clean
* @return string - Prettified XML string
*/
static prettify(xml: String) {
let pad = 0;
const padding = '\u0020'.repeat(4); // set desired indent size here
xml = xml.replace(/(\r\n|\n|\r)/gm, '\u0020').replace(/>\s+</g,'><');
xml = xml.replace(/(>)(<)(\/*)/g, '$1\r\n$2$3');
return xml.split('\r\n').map((node, index) => { //XML elements now split into lines
let indent = 0;
if (node.match(/.+<\/\w[^>]*>$/)) {
indent = 0;
} else if (node.match(/^<\/\w/) && pad > 0) {
pad -= 1;
} else if (node.match(/^<[\w^>]*[^\/]>.*$/)) {
indent = 1;
} else {
indent = 0;
}
pad += indent;
return padding.repeat(pad - indent) + node;
}).join('\r\n');
}
jlyk: if (node.match(/.+<\/\w[^>]*>$/)) {
- this is extremely slow on big rows.
not for production use for sure ;) - DannyDainton/newman-reporter-htmlextra#428 (comment)
I've just come across a small issue with CDATA sections. If these contain tags then this puts then on new lines introducing white space which can break the content of the CDATA section where white space may already be deliberately encoded.
eg. Here the CDATA section is protecting carriage returns contained in the string:
<!{CDATA[line 1
line 2
line 3
]]>
But after "pretty printing" it ends up as:
line 2 line 3 ]]>which has changed the meaning of the text the CDATA section was enclosing.
Cool function in all other cases though! :-)