자바 객체와 XML간의 변환에관해서는 몇 가지 라이브러리들이 존재하고 있습니다만 이번 시간에는 Java SDK에
기본으로 포함되어 있는 java.beans.XMLDecoder와 java.beans.XMLEncoder를 이용하여 자바객체<->XML간
변환 방법을 알아보겠습니다.
java.beans.XMLDecoder와 java.beans.XMLEncoder클래스는 J2SE 1.4 버전부터 이용할 수 있습니다.
우선, XML로 변환하고 XML로부터 복원할 적당한 자바 클래스를 작성합니다.
[code]
/*
* Setter와 Getter를 가진 전형적인 JavaBean 클래스입니다.
*/
package com.yunsobi.beanxmltranslate;
import java.awt.Point;
import java.util.ArrayList;
/**
*
* @author 신윤섭
*/
public class SampleBean {
private int[] scores;
private String name;
private Point seat;
private ArrayList entry;
public int[] getScores() {
return scores;
}
public void setScores(int[] scores) {
this.scores = scores;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Point getSeat() {
return seat;
}
public void setSeat(Point seat) {
this.seat = seat;
}
public ArrayList getEntry() {
return entry;
}
public void setEntry(ArrayList entry) {
this.entry = entry;
}
}
[/code]
보시면 아시겠지만 int 배열과 String, Point, ArrayList 객체를 갖는 전형적인 자바빈 객체 하나를 만들었습니다.
이제, 이 SampleBean 클래스를 xml로 변환하고 변환한 xml로 부터 SampleBean객체를 복원하는 코드를 작성합니다.
[code]
/*
* java.beans.XMLDecoder와 java.beans.XMLEncoder는
* Java객체를 XML로
* XML에서 Java 객체로 변환하는 쉬운 방법을 제공 합니다.
*/
package com.yunsobi.beanxmltranslate;
import java.awt.Point;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
/**
*
* @author 신윤섭
*/
public class BeanXmlTranslator {
public static void main(String args[]) throws Exception {
//xml로 변환할 클래스를 생성
SampleBean sample = new SampleBean();
//파라메터를 세팅해 봅시다.
sample.setScores(new int[]{100, 90, 75}); //int[]
sample.setName("Gore"); //String
sample.setSeat(new Point(5, 3)); //java.awt.Point
//ArrayList에 String과 Point도 넣어보구요..
ArrayList entry = new ArrayList();
entry.add("우리는");
entry.add("하나다");
entry.add(new Point(1,1));
sample.setEntry(entry);
//자.. 객체를 XML로 변환 시킵니다.
XMLEncoder encoder = new XMLEncoder(
new BufferedOutputStream(
new FileOutputStream("C:\\Sample.xml")));
encoder.writeObject(sample);
encoder.close();
//객체 레퍼런스를 찍어보구요..
System.out.println(sample);
//이젠 XML에서 객체로 복원시켜 봅니다.
XMLDecoder decoder = new XMLDecoder(
new BufferedInputStream(
new FileInputStream("C:\\Sample.xml")));
SampleBean sample2 = (SampleBean) decoder.readObject();
decoder.close();
//객체 레퍼런스를 찍어보구요..
System.out.println(sample2);
}
}
[/code]
예.. 변환의 핵심은 XMLEncoder 클래스의 writeObject() 메소드와 XMLDecoder 클래스의 readObject() 메소드
에 있습니다.
객체를 xml로 변환한 결과를 한번 볼까요?
[code]
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0_13" class="java.beans.XMLDecoder">
<object class="com.yunsobi.beanxmltranslate.SampleBean">
<void property="entry">
<object class="java.util.ArrayList">
<void method="add">
<string>우리는</string>
</void>
<void method="add">
<string>하나다</string>
</void>
<void method="add">
<object class="java.awt.Point">
<int>1</int>
<int>1</int>
</object>
</void>
</object>
</void>
<void property="name">
<string>Gore</string>
</void>
<void property="scores">
<array class="int" length="3">
<void index="0">
<int>100</int>
</void>
<void index="1">
<int>90</int>
</void>
<void index="2">
<int>75</int>
</void>
</array>
</void>
<void property="seat">
<object class="java.awt.Point">
<int>5</int>
<int>3</int>
</object>
</void>
</object>
</java>
[/code]
객체의 타입과 할당된 값이 깔끔한 xml문서로 작성된것을 확인 하실 수 있습니다.
자바에는 객체 직렬화(Serialize) 란게 있지요..
위 XMLDecoder와 XMLEncoder가 직렬화 기능을 이용하는건지 아닌지 확인 해 볼까요?
가장 정확한 방법은 XMLDecoder와 XMLEncoder의 소스를 보는것이겠지만 이미 위 예제 소스만으로도
충분히 예상 하실 수 있습니다. 위 예제의 SampleBean 이 Serializable 하지 않음에도 xml문서로 생성되었습니다.
예.. XMLDecoder와 XMLEncoder 내부적으로 java Reflection을 이용하여 xml, 객체간 변환을 수행하고 있습니다.
java Reflection을 이용한다고 했습니다. 이는 getXXX, setXXX 메소드가 없다면 해당 변수를 xml로 만들어내지
못하고 객체로 복원해 내지도 못함을 의미합니다.
또 하나, XMLEncoder를 통해 변환된 xml은 문서의 형태로 보아도 외부 시스템과의 정보의 교환보다는
객체의 상태저장/복원을 목적으로 사용하는게 더 어울릴거란 생각이 드네요.
관리자만 볼 수 있는 댓글입니다.
그냥 가려다...
call by value 증명으로 obj 에 null 을 대입했는데..
obj = null; 대입하지 말고 obj.setText("변경전")
이렇게 대입해보면 tc 값이 바뀌는걸 알 수 있습니다.
레퍼런스 변수(참조변수)을 함수의 인자로 넘기면 힙에 저장된 실객체는 복사를 안합니다. 참조 값만 넘겨주지요..
call by value 냐 call by value냐 call by reference 냐가 중요한게 아니라
함수의 인자로 변수가 넘어 왔을 때 그 변수를 통해서 실객체의 값을 변동할 수 있느냐 못하냐 입니다.
위에서는 변동할 수 있다가 정답입니다.
다른 초보 개발자가 이글 보고 잘 못 인식할까봐 몇 자 적었습니다.
메소드에 인자 전달할때는 래퍼런스를 복제해서 넘긴다고 알고있는데 아닌가요?
테스트 결과는 당연한데 풀이가 아리송 하군요.
jvm의 동작과관련하여 좀 더 공부해 보셔야 할듯하네요... 게다가 Java의 대표적인 Immutable 객체 String을 call by value, call reference 의 예로들고 있는 점 또한 큰 오류라 생각합니다. 지나가다.....
잘알고 포스팅 하세요.완벽히 잘못된 예제 입니다
맞는 포스팅인 것 같은데 뭐가 잘못되었다는건지?
잘못된 예제가 이에용
http://trypsr.tistory.com/74
이 글을 참고 하세요.
제대로 된 예제가 맞는데요..
call by reference는 결국 주소값이 call by value로 전달되기 때문에 발생하는거죠.
그렇게 따지면 C언어도 Call by value인 셈이 되는 겁니다.
포인터의 값도 결국은 값을 넘기는거니까요.
call by reference의 의미는 해당 변수의 주소 값을 인자 값으로 넘기는 것을 의미 합니다.
위의 예제에서도 tc의 주소값을 넘기게 되므로 call by referece가 됩니다.
cbv.changeObject(tc);
를 통해,
public void changeObject( TargetClass obj)
obj가 tc의 주소값을 가지게 되어
obj의 내용을 수정하게되면 tc의 내용도 수정되겠지만
obj = null;을 하게되면
obj가 가르키는 주소값이 null 값으로 바꾸게 되어 가리키는 곳을 잃게 됩니다.
윗분들 말대로 잘못된 예제이며 tc.setText로 해서 문자열을 변경하는걸로 테스트 하는게 옳은 걸로 보입니다.
결론요약, 아래 링크 참고
http://stackoverflow.com/questions/17419257/does-java-use-call-by-value-or-call-by-reference
-----------------------------------------------------
http://trypsr.tistory.com/74 // 파워 오답
위 블로그 주소글처럼 이해하고 c++ 넘어와보세요.
c++의 *와 &만나는순간 오지게 털리니까.
현재 블로그글이 올바르게 이해한 예제입니다. 프로그램 한번만 짜서 돌려보면 버그 나서 탈탈 털리겠구만, 탁상공론으로 올바른 글 잘못된 예제라고 몰아붙이는 사람들때문에 글 올립니다.
행인13/Lele/지나가는/대박님이 올린글은 잘못된 글입니다. c/c++에서 & 선언의 의미를 제대로 이해하지 못한거 같군요. 그 중에서 대박님은 c/c++의 개념조차 흔들리고 계시네요.
c/c++에서의 changeObject( TestClass* a );
의 경우 call by value가 맞습니다.
changeObject( TestCalss& a );
의 경우는 call by reference 입니다.
혹시나 물어보겠습니다.
changeObject( TestClass*& a ); 는 c/c++에서 call by value 일까요 call by reference일까요? call by reference입니다. 포인터의 레퍼런스입니다. 바로위의경우는 클래스 인스턴스의 레퍼런스이고, 맨위는 클래스 인스턴스의 포인터값입니다.
첨언하자면, 자바에는 레퍼런스할 수 있는 방법을 제공조차 하지 않기 때문에 이해가 힘들고 다른 언어와 같이 비교하셔야 이해가 가능합니다. ( c#은 ref라는 명령어로 존재하지만 java에는 레퍼런스 연산자 자체가 없습니다. 자바에서 c/c++의 &에 해당하는 연산자가 있던가요? )
3차원을 사는 저희가 4차원의 모습을 상상하지 못하듯, 레퍼런스개념이 없는 자바만으로는 이해가 불가능합니다.
그리고 현인이 달을 가리키는데 손가락만 보고있네요. 이글의 핵심은 내가 원하는 instance의 멤버변수들을 제대로 변화시키는게 중요하지 call by value냐 call by reference냐 그게 중요하다고 적은글이 아닙니다. 동작 원리를 이해하는게 더 중요합니다. 그래야 내가 원하는 동작을 얻어낼수있으니까.
2003년에도 올라온 참으로 해묵은 논쟁이네요.
http://okjsp.net/seq/32431
하아.. 이거 신기하네요. 저도 저게 call by reference 라고 생각해왔는데.. 이 글을 보고 테스트 해보니 실제로 (주소)값 복사가 이루어지는군요.
아마도
1. tc의 실제 값을 저장한 메모리 공간이 있고
2. tc는 그 메모리 공간에 대한 주소값을 값으로 가진 변수이고
3. changeObject()의 인자로 들어온 obj 변수는 새로 생성된 변수이고
4. obj 변수에는 tc의 실제 값의 메모리 주소값에 대한 복사가 이루어진다.
이해는 한 듯한데, 하나 궁금한 것은 왜 자바가 이런식으로 구현되었을까 하는 점이네요..
이렇게 함으로써 얻는 장점이 있을텐데 말이죠.
잘보고 갑니다.
8년전 글이네요.
작성자분도 그만큼 경험이 생기셨으니 이제는 이해하시겠지만 몇 자 적어봅니다.
자바의 기본동작이 PBV니 PBR하는 글은 이미 수도없이 바왔습니다.
실재로 많은 전문가들이 PBV라고 확인했기에 이에 토를 달고 싶지는 않네요.
그런데 자바를 실재로 사용해보면 PBR처럼 동작하는 경우를 많이 보 실 수 있습니다.
이상하지요.(ㅎㅎ웃음) 네 저도 이상하다고 생각합니다. 어던 메카니즘으로 움직이길래 이럴까요?
솔직히 저도 잘 모릅니다. 다만 오브젝트를 상속받은 객체들에게서 자주 보입니다.
이제는 동작방식을 놓고 싸우지 마시고 자바의 동작원리를 이해하시면 됩니다.
primitive 타입의 변수와 immutable 객체들은 실 데이터들을 직접 가지고 있는 녀석들이죠.
다른 데이터를 할당하면 객체가 새로 생성되어버립니다. 이전의 값들은 어딘가에서 떠돌다가 GC에 의해서 사라져버립니다.
list나 map같은 주소를 기반으로 동작하는 객체들은 좀 다릅니다.
변수의 목적이 값을 저장하기 위함인데, 제 생각으로는 값을 저장하기 위한 목적 보다는 값들을 저장하거나 또는 값들간의 관계를 연결하기 위함이라고 생각합니다.
때문에 위와같은 객체들은 PBR처럼 보입니다. PBR이라고 생각해도 됩니다. 프로그래밍을 해보면 그렇게 동작하거든요.
각 변수들이 동작하는 방식을 이해하고 적당한 방법으로 그것을 사용할 줄 아는게 더 중요하다고 생각합니다.
어차피 PBV, PBR 개념을 알고 그에 맞게 사용하면 되는거고, 각 변수들이 어떤식으로 동작하는지 알면 되는겁니다.
지나가던 신입개발자인데, 많은 토론이 오고가는것 같아 답글 남겨봅니다.
우선 위에서 정리되었듯 자바는 CBV가 맞습니다.
다만, Call By Reference 처럼 동작한다고 여겨지는 이유는, 객체의 값에 대한 복사가 아닌 참조변수의 값에 대한 복사가 이루어지기 때문입니다.
위 코드를 예시로 들자면 tc라는 참조변수는 "tc가 참조하는 객체의 주소"를 stack 에 저장합니다. 그리고 이 저장된 "객체의 주소값" 또한 stack 에서의 주소를 가지게 되죠.
("stack 에서의 주소", "tc가 참조하는 객체의 주소") 를 ("tcRef", "rValue")라고 부르겠습니다.
이때 이 tc를 함수의 인자로 넘겨주면, 우선 tcRef를 통해 stack 에 접근합니다, 이때 rValue를 얻게되는데요. 이 rValue라는 값을 복사한 뒤, changeObject의 인자인 obj로서 할당합니다. 즉 obj라는 참조변수가 생기고, 이 참조변수는 똑같이 rValue를 값으로 하여 stack 에 저장됩니다. 즉 ("objRef", "rValue")형태가 새로 생기는거죠.
그렇기때문에 결과적으로 obj와 tc는 같은 인스턴스를 참조하는 서로 다른 두 참조변수가 되는 것 입니다.
윗분중, 이를 CBV라고 칭할 경우 C언어도 CBV에 해당하는지에 대해 의문을 제기해주신 분이 계셧는데요.
C언어는 포인터를 통해 tc 라는 참조변수에 대한 주소값, 즉 tcRef 자체를 인자로서 넘길 수 있습니다. 이때 포인터 형식 즉 주소형식으로 보낼 수 있기 때문에 CBR이라고 칭하는 것 입니다.
Java에서는 소스코드상에서 C의 포인터처럼 실제 stack의 메모리값을 변수로서 저장하거나(*) 이 값을 통해 메모리에 저장된 값을 가져오기 (&)어렵습니다. (적어도 제가 아는 방법중에서는 없어요. reflection 쪽 기술중에 있을 수 도 있어서 확실친 않습니다.)
따라서 Java가 자연스럽게 CBV방식을 따르게 되었다 라고 인지하시면 될 것 같습니다.
이미 어느정도 정리된 토픽에 많이 늦게 참여하는듯 해서 조심스럽네요. 다들 추운 겨울따뜻하고 좋은 하루 보내시길 바라며 글을 마칩니다.