2011년 1월 12일 수요일

javascript Map Json

원리대로 JSON을 만드는
{"id":"atspeed","name":"송성일"}
이런식은 교범에나 나오는 방법으로 실무에서는 그닥 사용 빈도가 높지 않다.

보통은 <script type="text/javascript" src="json2.js"></script>을 이용해서

function User(id, name) {
this.id = id;
this.name = name;
}

function userJsonString(){
var user = User("atspeed","송성일");
var jsonString = JSON.stringify(user);
return jsonString;
}

Json String으로 변환을 하기 위해 먼저 만든 Map을 그냥 사용한다면 아래와 같은 문제가 발생한다.
var map = new Map();
  map.put("t1", "test1");
  map.put("t2", "test2");
  map.put("t3", "test3");
  map.put("t4", "test4");
  map.put("t5", "test5");

  var map1 = new Map();
  map1.put("m1", "map1");
  map.put("m1", map1);

  var map2 = new Map();
  map2.put("m2", "map2");
  var map3 = new Map();
  map3.put("m3", "map3");
  var list = new Array();
  list[0] = map2;
  list[1] = map3;

  map.put("list", list);

map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"map":{"m1":"map1"}},"list":[{"map":{"m2":"map2"}},{"map":{"m3":"map3"}}]}

즉, Map객체의 this.map이 m1의 아래 나온다.
"m1":{"map":{"m1":"map1"}}
우리가 원하는 결과는"m1":{"m1":"map1"} 이거다.

이 문제를 해결하기 위해 예전에 사용하던 방법은
 typeof this.map[prop] 를 이용해 타입별로 jsonString을 생성하는 거였다.

하지만 좀더 쉬운 방법으로
putMap : function(key, value){
     this.map[key] = value.map;
    },
함수를 추가하는 거다.

위의 예제에서 map.put("m1", map1); 부분을 map.putMap("m1", map1); 으로 바꾸면
map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"m1":"map1"},"list":[{"map":{"m2":"map2"}},{"map":{"m3":"map3"}}]}
원하는 결과가 나온다.

list또한 조금 문제가 될수 있지만 스팩만 이해 한다면 사용하는데는 문제가 없다.
그래도 바꾸고자 한다면,
putMapList : function(key, value){
     var list = new Array();
     for(var i=0;i<value.length;i++){
      list.push(value[i].map);
     }
     this.map[key] = list;
    },
를 추가한다.

사용법은 map.putMapList("list", list);
map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"m1":"map1"},"list":[{"m2":"map2"},{"m3":"map3"}]}
깔끔한 jsonString을 사용할수 있다.

결과적으로
Map = function(){
 this.map = new Object();
};  
Map.prototype = {  
    put : function(key, value){  
        this.map[key] = value;
    },
    putMap : function(key, value){
     this.map[key] = value.map;
    },
    putMapList : function(key, value){
     var list = new Array();
     for(var i=0;i<value.length;i++){
      list.push(value[i].map);
     }
     this.map[key] = list;
    },
    get : function(key){  
        return this.map[key];
    },
    containsKey : function(key){   
     return key in this.map;
    },
    containsValue : function(value){   
     for(var prop in this.map){
      if(this.map[prop] == value) return true;
     }
     return false;
    },
    isEmpty : function(key){   
     return (this.size() == 0);
    },
    clear : function(){  
     for(var prop in this.map){
      delete this.map[prop];
     }
    },
    remove : function(key){   
     delete this.map[key];
    },
    keys : function(){
        var keys = new Array();
        for(var prop in this.map){
            keys.push(prop);
        }
        return keys;
    },
    values : function(){  
     var values = new Array();  
        for(var prop in this.map){  
         values.push(this.map[prop]);
        }  
        return values;
    },
   size : function() {
    var count = 0;
    for (var prop in this.map) {
      count++;
    }
    return count;
   },
    jsonString: function(){
     return JSON.stringify(this.map);   
    }
};
사용법은
function userJsonString(){
 var map= new Map();
 map.put("id", "atspeed");
 map.put("name", "송성일");
 return map.jsonString();
}

ajax를 태울때는 그냥
function sendUser(){
 var map= new Map();
 map.put("id", "atspeed");
 map.put("name", "송성일");
 sendJsonAjax("user.json", map, "userResult");
}

function userResult(json){
 ...
}

var SEND_JSON_TIMEOUT = 5000;
function sendJsonAjax(sendUrl, map, successFunction, failFunction){
 $.ajax({
  type: "post",
  url: sendUrl,
  data: map.jsonString(),
  dataType: "json",
  timeout: SEND_JSON_TIMEOUT,
  error: function(a, b, c){        
         if(failFunction != null){
          eval(failFunction+"(a,b,c)");
         }else{
          alert('오류가 발생했습니다. 다시 시도해 주십시요.'+ a +' / ' + b +' / ' + c);
         }
     },
     success: function (json) {
      if(successFunction != null){
       eval(successFunction+"(json)");
      }
     }
 });
}

물론 Map에 다른 Map이나 Array를 담아도 문제가 없다.

var map = new Map();
  map.put("t1", "test1");
  map.put("t2", "test2");
  map.put("t3", "test3");
  map.put("t4", "test4");
  map.put("t5", "test5");

  var map1 = new Map();
  map1.put("m1", "map1");
  map.putMap("m1", map1);

  var map2 = new Map();
  map2.put("m2", "map2");
  var map3 = new Map();
  map3.put("m3", "map3");
  var list = new Array();
  list[0] = map2;
  list[1] = map3;

  map.putMapList("list", list);

map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"m1":"map1"},"list":[{"m2":"map2"},{"m3":"map3"}]}

그냥 일반적인 jQuery를 이용한 ajax사용법이나, callBack받은 json데이터의 사용법은 생략한다.

일반적인 내용은 나 말고도 더 정리 잘하는 분들이 계시니 그분들의 글을 활용하시면 될듯.
하지만 필자가 구글링을 아무리 해도 정리된 내용의 이해가 어려운 경우는 내가 이해한 방식으로 글을 올리도록 해야겠죠?

#SCMInno #에스씨엠이노 #WEBDeK

javascript Map HashMap

요즘 javascript의 대세는 역시 jQuery일것이다.
jQuery를 사용해 보니 가볍고 편리한게 장점인듯하다.

그래도 노가다 코딩을 워낙 싫어라 하는 탓에 jQuery를 한꺼플씩 포장해서 사용하고 있다.

제목과는 전혀 맞지 않는 서두라 죄송...^^

아무튼 구글링을 하면 관련 자료들이 많이 나오지만 사용중인 코드는 이거다.

Map = function(){
 this.map = new Object();
};  
Map.prototype = {  
    put : function(key, value){  
        this.map[key] = value;
    },  
    get : function(key){  
        return this.map[key];
    },
    containsKey : function(key){   
     return key in this.map;
    },
    containsValue : function(value){   
     for(var prop in this.map){
      if(this.map[prop] == value) return true;
     }
     return false;
    },
    isEmpty : function(key){   
     return (this.size() == 0);
    },
    clear : function(){  
     for(var prop in this.map){
      delete this.map[prop];
     }
    },
    remove : function(key){   
     delete this.map[key];
    },
    keys : function(){  
        var keys = new Array();  
        for(var prop in this.map){  
            keys.push(prop);
        }  
        return keys;
    },
    values : function(){  
     var values = new Array();  
        for(var prop in this.map){  
         values.push(this.map[prop]);
        }  
        return values;
    },
    size : function(){
      var count = 0;
      for (var prop in this.map) {
        count++;
      }
      return count;
    },
    toString : function(){
      var s=[];
      for(var prop in this.map){
         s.push(prop+':'+this.map[prop]);
      }
      return s.join(',');
    }
};

사용법은 간단하다.
var map = new Map();
 map.put("user_id", "atspeed");
 -----------------------
map.get("user_id");

그냥 java Map과 동일하게 사용하면 되는거다.

#SCMInno #에스씨엠이노 #WEBDeK

2011년 1월 4일 화요일

ibatis JSON 연동

프레임워크를 사용하다보면 가장 큰 문제는 데이터 옮겨담기다.

예를 들어 ibatis에 리턴한 형태가 Map인데 이걸 다시 bean에 옮겨 담아야 한다면 불필요한 리소스를 사용하게 될것이다.

JSON방식으로 Ajax를 구현하면서 발생한 문제가 이거다.

실제로 ibatis에서 결과를 JSONObject형태로 받을 수 없기 때문이다.
resultClass로 JSONObject를 선언한다 하더라고 getter, settger가 없는 일반 Object이기 때문에 오류가 발생한다.
그렇다고 JSONObject가 Map을 implement하거나 HashMap을 상속받은 구조도 아니다.
결국 직접 사용하기 위해 만들어야 했다.

package com.atspeed;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.json.JSONObject;
public class JSONMap extends JSONObject implements Map{
 @Override
 public int size() {
  // TODO Auto-generated method stub
  return super.length();
 }
 @Override
 public boolean isEmpty() {
  // TODO Auto-generated method stub
  return (super.length()==0?true:false);
 }
 @Override
 public boolean containsKey(Object key) {
  // TODO Auto-generated method stub
  return !super.isNull(key.toString());
 }
 @Override
 public boolean containsValue(Object value) {
  // TODO Auto-generated method stub
  Iterator it = super.keys();
  while(it.hasNext()){
   String key = it.next().toString();
   try {
    if(super.get(key) == value){
     return true;
    }
   } catch (Exception e) {
    // TODO: handle exception
   } 
  }
  return false;
 }
 @Override
 public Object get(Object key) {
  // TODO Auto-generated method stub
  try {
   return super.get(key.toString());
  } catch (Exception e) {
   // TODO: handle exception
  }
  return null;
 }
 @Override
 public Object put(Object key, Object value) {
  // TODO Auto-generated method stub
  try {
   return super.put(key.toString(), value);
  } catch (Exception e) {
   // TODO: handle exception
  }
  return null;
 }
 @Override
 public Object remove(Object key) {
  // TODO Auto-generated method stub
  return super.remove(key.toString());
 }
 @Override
 public void putAll(Map m) {
  // TODO Auto-generated method stub  ;
  Iterator it = m.keySet().iterator();
  while(it.hasNext()){
   String key = it.next().toString();
   try {
    super.put(key, m.get(key));
   } catch (Exception e) {
    // TODO: handle exception
   } 
  }
 }
 @Override
 public void clear() {
  // TODO Auto-generated method stub
  Iterator it = super.keys();
  while(it.hasNext()){
   String key = it.next().toString();
   super.remove(key);
  }
 }
 @Override
 public Set keySet() {
  // TODO Auto-generated method stub
  return null;
 }
 @Override
 public Collection values() {
  // TODO Auto-generated method stub
  return null;
 }
 @Override
 public Set entrySet() {
  // TODO Auto-generated method stub
  return null;
 }
}
사실 구현해야할 몇가지 Map관련 함수는 빼먹었다.

이걸 사용하는 방법은

SqlMap.xml쪽에서
<typeAlias alias="JSONMap" type="com.atspeed.JSONMap"/>
 <select id="json_list" parameterClass="Map" resultClass="JSONMap">

이것만 해주면 된다.
결론적으로 ibatis에서Map으로 인정받고 사용하는거다.
결국 Service나 Control단에서는 결과는 JSONMap으로 꺼내 사용하면되는것이고.
실제 JSONObject를 상속받았기 때문에 사용법은 JSONObject와 동일하다.

#SCMInno #에스씨엠이노 #WEBDeK

android ListView CheckBox onListItemClick

영어 실력이 안드로메다인지라 제목에 그냥 키워드를 나열했군요...ㅡㅡ;;;

이 글의 진정한 제목은
CheckBox를 포함한 ListView의 onListItemClick 발생시키기다.

ListView의 Item에 CheckBox가 포함된 경우 onListItemClick이벤트가 발생하지 않는다.
그건 아마도 CheckBox가 이벤트를 잡수시는 엄청난 포스가 있으신듯하다.

그리하여 결국 내가 선택한 방법은 CheckBox를 클릭할때 ListView의 onListItemClick를 강제로 발생시키는 방법이다.
머 이런 경우는 웹에서도 종종 발생하는 경우이기도 하다.
여러 컴퍼넌트에서 이벤트가 발생하면 뜻하지 않은 이상한 결과가 발생하는건 당연한듯하다.

결론은(클래스명도 안드로메다임을 알아주시길...)

import android.view.View;
import android.widget.ListView;
public class ListInCheckItemClickAdapter implements View.OnClickListener {
 ListView listView;
 int position;
 int id;
 public ListInCheckItemClickAdapter(ListView listView, int position, int id){
  this.listView = listView;
  this.position = position;
  this.id = id;
 }

 @Override
 public void onClick(View v) {
  // TODO Auto-generated method stub
  listView.performItemClick(v, position, id);
 }
}

여기서 핵심은
listView.performItemClick(v, position, id);
이벤트를 강재로 발생시키는 함수는 perform으로 시작한다.

그렇다면 만들어논 클래스를 사용하는 방법은
ListView에 연결된 BaseAdapter의 getView에서 Item생성시 CheckBox에 clickEvent로 이 객체를 연결하는 방식이다.

checkBox.setOnClickListener(new ListInCheckItemClickAdapter(getListView(), position, checkBox.getId())); 사용법은 간단하다.
getListView()는 아래 글중에 한번 언급한듯 하다.
ListActivity를 상속받은 경우 ListView를 가져오는 함수다.

참고로 BaseAdapter는 inner class로 구현했다.

사실 BaseAdapter도 자주 쓰는 부분 getCount, getItem, getItemId, getView등의 중복코딩이 있어 별도로 구현한 Adapter를 사용한다.
내 경우는 반복코딩이나 무한 삽질을 워낙 싫어하다 보니 구현해서 사용하지만 개발자마다 취향이 있어 포스팅까지는 필요없다고 생각한다.

#SCMInno #에스씨엠이노 #WEBDeK