[js] java와 연동시 JSON처리의 문제

스프링 등을 REST빵셔틀로 쓰면 그쪽에 있는 JSON인코더를 통해 클라에게 데이터를 내리기 마련입니다.
헌데 헉소리나는 현상을 발견했습니다! 개행문자열을 포함한 데이터를 내려보낼때 개행을 그냥 포함해서 JSON문자열을 만들어내 쏘고 있다는거죠.

Screenshot_7

바로 이런 상황인데 Contents 항목의 값이 저렇게 오는 현상입니다.

그렇게 되면 자바스크립트의 내장 JSON.parse함수는 이를 파싱하지 못하고 런타임에러를 토하면서 죽어버립니다.
(물론 제가 서버코드를 수정할 수 있는 상황이었다면 서버를 수정하면 될텐데…말이죠 ^^;)

 
 
 

개행을 치환하기

적당히 정규식전략을 세워보면

  1. : ” 로 시작하는데
  2. 마지막 문자열이 ” , { [ : 등으로 끝나지 않는 개행이라면
  3. {@} 정도로 치환하고 파싱한뒤
  4. 나중에 다시 개행으로 치환하려는 거죠.

이를 정규식으로 간단히 표현하면 다음과 같습니다.

var r0 = /[:][ ]?["].+[^"{[,: ]n/g

좀 더 정교하게 짜야할텐데, 귀찮아서 대략 막음..^^;

파싱함수를 작성해기 전에 개행을 치환해야하니 replace용 개행 정규식 = /n/g

이 필요합니다.
이제 재료가 다 모였으니 파싱함수를 작성합니다.

//위에 말한 정규식 두 개
var r0 = /[:][ ]?["].+[^"{[,: ]n/g, r1 = /n/g;

var parser = function(v){

	var t0 = '', t1, i, j, k = 0;

	//조건에 맞는 녀석을 찾는다
	//k는 무한루프 방지용 ^^;;
	while( ( i = v.search(r0) ) > -1 && k++ < 30 ){

		//값의 마지막은 ", 로 끝나니까..
		if( ( j = v.indexOf( '",', i ) ) > -1 ){

			//그 안에 있는 모든 개행은 {@}로 치환
			t1 = v.substring( i, j ).replace( r1, '{@}' );
			t0 += v.substring( 0, i ) + t1;

			//search는 indexOf처럼
			//n번째부터라는 옵션이 없어서
			//걍 v를 쳐내야함
			v = v.substr(j);
		}
	}

	//남은 조각과 함께 파싱!
	return JSON.parse( t0 + v );
};

var data = parser( data );

머 그럭저럭 돌아갑니다 ^^;

 
 
 

치환문자를 개행으로 역치환

개행을 {@}로 만들었으니 다시 파싱이 완료된 JSON을 돌며 개행으로 변환해줘야합니다. 루프로 짜면 좋지만 귀찮으니까 재귀로 짜는 걸로 하죠.

대략 전략은 다음과 같습니다.

  1. 그 값이 문자열이면 개행으로 치환한다.
  2. 오브젝트인데 null이 아니라면 각 요소를 돌면서 다시 변환함수로 재귀시킨다.
  3. 그외에는 그냥 쓰루패스

코드로 짜보면 다음과 같습니다.

//역으로 치환한 문자열인식용 정규식
var r2 = /[{]@[}]/g;

var loop = function(v){
	var i;
	switch( typeof v ){

	//문자열이면 개행치환
	case'string':return v.replace( r2, 'n' );

	case'object':
		//오브젝트고 null이 아니면 요소재귀
		if( v ){
			for( i in v ) v[i] = loop(v[i]);
			return v;
		}
	}

	//아니면 걍 토함
	return v;
};

간단하죠. 그럼 위에서 만들었던 오브젝트를 이 loop 함수 한 바퀴 돌리는 걸로 개행이 재치환 되어 나올 것입니다.

 
 
 

결론

정규식 좀 많이 다듬어야하는데, 급한대로 만들어서 막고 있습니다. 서버가 막는게 최선이긴한데 말이죠 ^^;

하지만 설령 서버가 막았다고 해도 여전히 그 개행을 치환해야하는 문제는 발생하기 때문에 서로 약속을 해서 개행 치환해야 하니 loop함수는 여전히 필요할 것입니다.

전체 코드는 다음과 같습니다.

var parser = (function(){

	var r0 = /[:][ ]?["].+[^"{[,: ]n/g, r1 = /n/g, r2 = /[{]@[}]/g,
	loop = function(v){
		var i;
		if( v ) switch( typeof v ){
		case'string':return v.replace( r2, 'n' );
		case'object':
			for( i in v ) v[i] = loop(v[i]);
			return v;
		}
		return v;
	};

	return function(v){
		var t0 = '', t1, i, j, k = 0;
		while( ( i = v.search(r0) ) > -1 && k++ > 30 )
			if( ( j = v.indexOf( '",', i ) ) > -1 )
				t1 = v.substring( i, j ).replace( r1, '{@}' ),
				t0 += v.substring( 0, i ) + t1,
				v = v.substr(j);
		return loop(JSON.parse( t0 + v ));
	};
})();

data = parser(data);