JSONの整形出力2007年05月15日 15時02分35秒

JSONの可読性

先のエントリで、オンラインJSONエディタのことを書いたが、それと関連。

dara-jは、個人的なスクリプト(WSH/HTA/ASP)をちょろちょろ書いているが、設定を要するものは今は大体JSONにしている。

ところがjson.jsなどのライブラリで出力すると、不要な空白や改行が除去されるため可読性が極端に悪く、テキストエディタでちょっと編集というわけには行かない。まぁそのおかげで処理が速いんだけど。

そこで、ちょっとでも編集しやすいようにと思い、整形出力するライブラリを書いてみた。

Json.Formatter

コードはちょっと大きめで申し訳ないが、以下のような感じ。

// 文字列を引用符で囲う
String.prototype.quote = function() {
	var s = this;
	var a = [
		{ match : /\\/g, replace : "\\\\" },
		{ match : /\f/g, replace : "\\f" },
		{ match : /\n/g, replace : "\\n" },
		{ match : /\r/g, replace : "\\r" },
		{ match : /\t/g, replace : "\\t" },
		{ match : /\v/g, replace : "\\v" },
		{ match : /"/g, replace : "\\\"" }
	];
	for(var i = 0; i < a.length; i++) {
		var value = a[i];
		s = s.replace( value.match, value.replace );
	};
	return [ "\"", s, "\"" ].join("");
}

if( typeof( Json ) == "undefined" ) {
	var Json = {
	}
}

Json.Formatter = function() {
	this.initialize.apply( this, arguments );
}
Json.Formatter.prototype = {
	value : false,
	
	// 初期化処理
	initialize : function(obj) {
		var indent_char = Json.Formatter.indentString || "\t";
		var name = arguments[1] ? arguments[1].toString().quote() : null;
		var indent = isNaN( arguments[2] ) ? 0 : Number( arguments[2] );
		
		var current_indent = Json.Formatter.createString( indent_char, indent );
		var buffer = [ current_indent ];
		
		if( name ) {
			buffer.push( name );
			buffer.push( " : " );
		}
		
		var type = typeof( obj );
		if( type == "undefined" ) {
			// undefined
			// なにもしない
			
		} else if( obj == null ) {
			// null
			// プロパティの値の場合のみ'null'を追加
			if( name ) buffer.push( "null" );
			
		} else if( type == "string" ) {
			// string
			buffer.push( obj.quote() );
			
		} else if( type == "number" ) {
			// number
			buffer.push( obj.toString() );
			
		} else if( type == "boolean" ) {
			// boolean
			buffer.push( ( !! obj ).toString() );
			
		} else if( obj instanceof Array ) {
			// array
			var hasProp = obj.length > 0;
			var lastIndex = obj.length - 1;
			
			buffer.push( "[" );
			buffer.push( hasProp ? "\r\n" : "" );
			// 要素を再帰処理
			for(var index = 0; index < obj.length; index++) {
				var v = obj[ index ];
				if( v != null ) {
					buffer.push( new Json.Formatter( v, null, indent + 1 ).value );
					buffer.push( index < lastIndex ? "," : "" );
					buffer.push( "\r\n" );
				}
			}
			if( buffer.length > 2 && buffer[ buffer.length - 2 ] == "," ) buffer[ buffer.length - 2 ] = "";
			buffer.push( hasProp ? current_indent : "" );
			buffer.push( "]" );
			
		} else {
			// object
			var hasProp = false;
			for(var key in obj) {
				if( typeof(obj) != "function" ) {
					hasProp = true;
					break;
				}
			}
			
			buffer.push( "{" );
			buffer.push( hasProp ? "\r\n" : "" );
			// プロパティを再帰処理
			for(key in obj) {
				if( typeof(obj[key]) == "function" ) continue;
				buffer.push( new Json.Formatter( obj[key], key, indent + 1 ).value );
				buffer.push( ",\r\n" );
			}
			if( hasProp ) buffer.length--;
			
			buffer.push( hasProp ? "\r\n" : "" );
			buffer.push( hasProp ? current_indent : "" );
			buffer.push( "}" );
			
		}
		
		// valueプロパティ確定
		this.value = buffer.join("");
		
		var self = this;
		this.toString = function() {
			return self.value;
		}
	}
}
Json.Formatter.createString = function(c, l) {
	var result = new Array( l );
	for(var i = 0; i < l; i++) result[ i ] = c;
	return result.join("");
}
Json.Formatter.indentString = "\t";
他のライブラリには依存していないのでこれ単体で使用できるが、文字列を引用符でくくるためにString.prototypeを拡張している。気になる向きは関数化するなどすれば問題ないと思う。コード中で使用しているのは2箇所のみだし。

使い方

コンストラクタにJSON化したいオブジェクトを渡してvalueプロパティを参照するだけ。

var obj = { a : "property A", b : 30, c : true, d : [ 0, 1, 2 ] };
var formatter = new Json.Formatter( obj );
alert( formatter.value );
// 以下のような出力を得る
/*
{
	"a" : "property A",
	"b" : 30,
	"c" : true,
	"d" : [
		0,
		1,
		2
	]
}
*/
インデントはタブで行われるが、Json.Formatter.indentStringプロパティを変更することで任意の文字列にできる。

サンプル

テキストボックスにJSONを書き込んで[format]をクリックすると整形します。整形結果をalertではなく元のテキストボックスに放り込むよう修正しました。(2007.5.15 19:19)

注意など

重い。小さなオブジェクトなら問題ないけど、100行単位のArrayとか、ネストが深いObjectとかをかませるとかなり重い。だれか効率よくなるようにアドバイスください><

あと、このライブラリはオブジェクト → JSONのみのライブラリです。JSON → オブジェクトは他のライブラリを併用するか、漢らしくeval()するかで対応してください。dara-jはjson.js使うけど。

ま、あまり需要ないかな。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※なお、送られたコメントはブログの管理者が確認するまで公開されません。

名前:
メールアドレス:
URL:
コメント:

トラックバック

このエントリのトラックバックURL: http://dara-j.asablo.jp/blog/2007/05/15/1509590/tb

※なお、送られたトラックバックはブログの管理者が確認するまで公開されません。