admin write
blogblogblogbloglocation loglocation logtag listtag listguest bookguest book
rss feed

XML DOM이라는 것은 XML 파일을 HTML로 불러들이는 것이다. (원래 엄밀히 말하면 그런 건 아니지만, 수행평가를 위해서 복잡하게 알 필요는 없어서 자세한 설명은 생략한다)

1학기 때는 Data Binding을 이용했으나, 이번에는 XML DOM을 이용해 보는 것이다. 뭐, 그게 그거다..


일단 대충 XML 파일을 만들어본다.

<?xml version='1.0' encoding='euc-kr'?>
<booklist>
   <book kind='수필'>
    <title>멋진남자 히라이켄</title>
    <author>정민호</author>
    <publisher>햄터리 출판사</publisher>
    <price>15000</price>
   </book>
   <book kind='시'>
    <title>홀롤로</title>
    <author>이호</author>
    <publisher>호이 출판사</publisher>
    <price>3000</price>
   </book>
   <book kind='생활서'>
    <title>대출의 정석</title>
    <author>강대한</author>
    <publisher>리드코프</publisher>
    <price>50000</price>
   </book>
</booklist>

이제는 Javascript를 이용하여 이 것을 HTML 파일에 띄워 볼 것이다.

여기서 Java의 객체 생성 기능을 새록새록 떠올려본다. Javascript는 Java라는 말이 들어있다 보니 Java스러운 부분이 있다.

<script language='JavaScript'>
var xmlDoc;
function btnOpen_Click()
{
 xmlDoc = new ActiveXObject('Msxml2.DOMDocument');
 xmlDoc.load('list.xml');
 var err=xmlDoc.parseError;
 if(err.errorCode) { alert('XML 문서 해석 실패 : '+err.reason); return; }
 ...
 }
</script>
...
<form name='form1'>
...
<input type='button' name='btnOpen' value='열기' onclick='javascript:btnOpen_Click()'/>
...
</form>

일부 소스는 생략했다.

new라는 것은 Java시간에 배웠는데, 객체를 생성하는 것이다. new Car, new Soonok 등 우리는 2학년 떄 지긋지긋하게 써 봤다.

ActiveXObject라는 객체를 이용, DOM 객체를 생성한다. 딴 거 필요없고 저렇게만 쓰면 된다.

다음은 load라는 method를 통해 파일을 불러온다. 나중에는 HTML에서 파일을 불러오면 이를 Javascript에 전달받아서 쓰게 되겠지만, 일단은 직접 파일명을 쳐 보자. 예제의 파일명은 'list.xml' 이다.

다음의 코드는 XML 로딩이 실패했을 경우 알려주는 것인데, parseError라는 속성을 별도의 변수에 저장한다. 이 속성은 에러가 났을 경우 에러 정보를 가지고 있다.
이 속성 하위에는 또 다른 속성이 있는데, errorCode라는 속성은 XML 로딩 시 에러 여부를 판별한다. 에러가 있을 경우 true, 없을 경우 false이다. if문을 통해 true일 경우 alert를 이용해 XML 해석이 실패했다는 것과 err.reason 이라는 속성을 통해 이유를 알려준다.

만약 XML 로딩이 성공적으로 완료되었으면 if문 다음부분을 통해 XML문서를 만지작할 수 있다.

우선, 처음 엘리먼트의 내용을 출력해 보자. 예제 XML 파일의 많은 내용 중 첫번째 엘리먼트의 내용은 '멋진남자 히라이켄' 이 된다. (kind='수필' 부분은 속성이다)

다음은 이 소스 전문(全文)이다.

<html><head>
<script language='JavaScript'>
var xmlDoc, eRoot;
function btnOpen_Click()
{
 xmlDoc = new ActiveXObject('Msxml2.DOMDocument');
 xmlDoc.load(form1.fileName.value);
 var err=xmlDoc.parseError;
 if(err.errorCode) { alert('XML 문서 해석 실패 : '+err.reason); return; }

 eRoot = xmlDoc.documentElement;
 var eBook = eRoot.firstChild;
 displayBook(eBook); // 함수 호출
}

function displayBook(eBook)
{
 
var eTitle = eBook.firstChild;
 
var tTitle = eTitle.firstChild;
 form1.txtTitle.value = tTitle.data;
}

</script>
</head><body>
<center>
<form name='form1'>
<h2>책 정보</h2>
<input type='file' size='30' name='fileName' />
<input type='button' name='btnOpen' value='열기' onclick='javascript:btnOpen_Click()'/>
<hr width='400' />
<table border='1' width='400' />
<tr>
<td bgcolor='#d0d0d0' width='100'>책제목</td>
<td><input type='text' name='txtTitle' size='40' /></td>
</tr>

</table>
</form>
</center>
</body></html>

실행 결과는 다음과 같다

책 정보


책제목


첫번째로 친 줄은 원래 소스에서 직접 파일 이름을 썼던 부분인데, HTML에서 파일을 읽어들일 수 있도록 'input type=file' 을 이용하여 HTML에서 파일을 불러들이게 했고, 그 파일 경로가 저장되어 있는 'form1.fileName.value' 속성을 기존의 'xmlDoc.load' 메소드에 인수로 넣으면 그 파일이 불러져 만지작할 수 있게 된다.

이번에 선언한 변수는 eRoot, eBook, eTitle, tTitle 4개이다.
eRoot는 말 그대로 루트, 그러니까 루트 엘리먼트인 <booklist> 태그 부분이다.
eBook은 XML 파일에서 <book> 태그 부분이다.
eTitle은 <book> 태그 밑에 있는 <title> <author> 등의 엘리먼트들이다.
tTitle은 eTitle에 있는 값을 가지고 있어서, tTitle.data 라는 메소드를 이용하여 이 값을 얻을 수 있다.

각각의 변수에 있는 firstChild 속성은 맨 처음 엘리먼트(태그) 를 가리킨다. 그러므로 무조건 맨 위에 있는 <booklist> - <book> - <title> - '멋진남자 히라이켄' 으로 가게 된다.


우리의 목표는 XML 파일의 모든 것을 만지작하는 것이다. 그런데 겨우 무조건 맨 첫번째 엘리먼트만 만지작거리겠다고 하는 건 말이 안 된다. 이젠 다음 엘리먼트를 만지작거릴 차례다.

여기서는 nextSibling이라는 속성을 이용한다. 이 속성은 다음 엘리먼트에 대한 속성을 담고 있다.

function displayBook(eBook)
{
 var eTitle = eBook.firstChild;
 var tTitle = eTitle.firstChild;
 form1.txtTitle.value = tTitle.data;
 eTitle = eTitle.nextSibling;
 tTitle = eTitle.firstChild;
 form1.txtAuthor.value = tTitle.data;
}
-------------------------------------------------------
<tr>
<td bgcolor='#d0d0d0' width='100'>책제목</td>
<td><input type='text' name='txtTitle' size='40' /></td>
</tr>
<tr>
<td bgcolor='#d0d0d0' width='100'>책저자</td>
<td><input type='text' name='txtAuthor' size='40' /></td>
</tr>

앞에서 빨갛게 칠해 진 부분을 이렇게 바꿔 nextSibling에 대해 알아보자.
먼제 책제목을 출력한 후, eTitle = eTitle.nextSibling을 통해 eTitle을 다음 엘리먼트, 즉 <title> 다음인 <author>에 위치시켰다. 데이터를 의미하는 tTitle은 데이터가 여러 개 있는 게 아니라 하나만 있으므로 firstChild로 한다. 그리고 출력.

책제목
책저자

이렇게 <author>에 있던 '정민호' 라는 내용도 출력할 수 있게 되었다. 이를 응용하면, <publisher>와 <price> 엘리먼트의 내용도 모두 출력할 수 있다. nextSibling을 계속 해주면 된다.

이제 맨 처음의 <book> 엘리먼트(멋진남자 히라이켄 등등)를 모두 출력할 수 있게 되었다. 그런데, XML 예제 파일을 보면 '멋진남자 히라이켄' 외에도 '홀롤로' '대출의 정석' 등 모두 3개의 <book> 엘리먼트가 존재한다. 이들을 모두 출력할 순 없을까.

답은 간단하다. <book> 하위의 <title> -> <author> -> <publisher>.. 로 가는 데 eTitle에 NextSibling을 걸어 주었으니, 이제는 이 상위인 eBook에 nextSibling을 걸어주면 된다. 우리는 이를 지난번의 데이터 바인딩처럼 버튼으로 만들어, <book> 엘리먼트를 넘나들도록 만들어 보려고 한다.

일단 만들고자 하는 버튼은 '이전' 과 '다음' 이다. '다음' 은 nextSibling을 이용하면 되고, '이전' 은 next가 아닌 '앞' 이라는 뜻의 previousSibling이라는 속성을 이용하면 된다. 버튼을 이용하려면 Javascript 함수를 만들어야 하므로, 스크립트 내에 '이전' 과 '다음' 의 역할을 하는 새로운 함수를 만들자.

<script language='JavaScript'>
var xmlDoc, eRoot, eBook;
function btnOpen_Click()
{
 xmlDoc = new ActiveXObject('Msxml2.DOMDocument');
 xmlDoc.load(form1.fileName.value);
 var err=xmlDoc.parseError;
 if(err.errorCode) { alert('XML 문서 해석 실패 : '+err.reason); return; }
 eRoot = xmlDoc.documentElement;
 eBook = eRoot.firstChild;
 displayBook(); // 함수 호출
}
function displayBook()
{
--------- <중략> -----------
}

function btnPrev_Click()
{
 eBook = eBook.previousSibling;
 displayBook();
}
function btnNext_Click()
{
 eBook = eBook.nextSibling;
 displayBook();
}
</script>

일단 eBook을 전역변수로 바꿔버림에 따라, displayBook의 인수를 없앴다. 인수가 없어도 eBook이 전역변수이므로 쓸 수 있기 때문이다.
'이전' '다음' 버튼을 누르는 두 개의 함수는 previousSibling과 nextSibling을 적용한 이후 displayBook()으로 변경된 것을 재출력하도록 했다.
그렇다면 이젠 버튼을 만들어 보자. 밑의 소스는 맨 마지막 </td>와 </table> 사이에 넣으면 된다.

<tr>
<td colspan='2' align='center'>
<input type='button' name='btnPrev' value='이전' onclick='javascript:btnPrev_Click()'/>
<input type='button' name='btnNext' value='다음' onclick='javascript:btnNext_Click()'/>
</td>
</tr>

이제 각 버튼을 눌러보면 '멋진남자 히라이켄'에서 '홀롤로' '대출의 정석' 으로 넘겨간 것을 확인할 수 있다.
그런데 만약, 맨 마지막 엘리먼트에서 nextSibling을 해 버리거나, 맨 처음 엘리먼트에서 previousSibling을 하게 되면 에러가 난다. 당연하지만 다음에 나올 데이터가 없기 때문에 에러가 나는 것이다.

이러한 것을 막으려면 현재 있는 eBook이 첫 또는 엘리먼트와 같은지의 여부를 조사해서 같으면 Sibling을 안 해버리면 된다.

function btnPrev_Click()
{
 if(eBook != eRoot.firstChild)
 {
  eBook = eBook.previousSibling;
  displayBook();
 }
}
function btnNext_Click()
{
 if(eBook != eRoot.lastChild)
 {
  eBook = eBook.nextSibling;
  displayBook();
 }
}

여기서 lastChild라는 속성이 새로 나오는데, firstChild이 맨 처음을 가리키는 속성이라면, lastChild은 말 그대로 맨 마지막 자식 엘리먼트를 가리키는 속성이 된다. 이것이 같으면, 결국 처음이나 마지막이라는 얘기다.

여기서 응용하여, '처음' '마지막' 버튼도 만들어 보자. eRoot.firstChild와 eRoot.lastChild를 이용하면 된다.

function btnFirst_Click()
{
 eBook = eRoot.firstChild;
 displayBook();
}
function btnLast_Click()
{
 eBook = eRoot.lastChild;
 displayBook();
}

...

<input type='button' name='btnFirst' value='처음' onclick='javascript:btnFirst_Click()'/>
<input type='button' name='btnLast' value='마지막' onclick='javascript:btnLast_Click()'/>

이제 처음과 마지막 버튼을 눌러보면 정상 작동되는 것을 확인할 수 있다.

여기서 우리는 잊은 게 있었다. <book kind = ''>
속성 부분인데, 아무래도 엘리먼트 내에 있는 '멋진남자 히라이켄' 같은 데이터가 아니다 보니 보통의 방법으로는 할 수 없고, 새로운 메소드를 써서 속성값을 가져오면 된다.

displayBook() 함수에 다음을 추가한다.

form1.txtKind.value = eBook.getAttribute('kind');

getAttribute라는 메소드는 엘리먼트의 속성을 가져오기 위한 메소드이다. 'kind' 라는 인수를 넣어 kind속성을 불러들여온다. 이 함수에서 form1.txtKind라고 하였으므로 역시 HTML 안의 form 태그 내에 다음을 추가한다.

<tr>
<td bgcolor='#d0d0d0' width='100'>책종류</td>
<td><input type='text' name='txtKind' size='40' /></td>
</tr>

한번 실행해 보자. 이젠 모든 내용을 출력할 수 있으며, 엘리먼트간 자유로운 이동도 할 수 있다.


책 정보


책제목
책종류
책저자
출판사
책가격
* 여기서는 버튼을 눌러도 작동되지 않는다.

10rwebp #4

2007. 4. 20. 12:28

지난주에 만들었던 booklist
employee에는 없는 새로운 개념이 다수 있으니 참고

- booklist.xml -

<?xml version="1.0" encoding="euc-kr"?>

<!-- XSL 문서 적용 -->
<?xml-stylesheet type="text/xsl" href="booklist.xsl"?>

<booklist>
   
  <book id="b1" kind="computer">
    <title>JSP And Servlet</title>
    <author>이규미</author>
    <publisher>인포북</publisher>
    <price>25000</price>
  </book>

  <book id="b2" kind="computer">
    <title>Inside XML</title>
    <author>신민철</author>
    <publisher>디지털북스</publisher>
    <price>35000</price>
  </book>

  <book id="b3" kind="language">
    <title>쉽게 배우는 영어</title>
    <author>채규태</author>
    <publisher>시사영어사</publisher>
    <price>20000</price>
  </book>

  <book id="b4" kind="computer">
    <title>XML 전자상거래</title>
    <author>이종호</author>
    <publisher>정보문화사</publisher>
    <price>28000</price>
  </book>

  <book id="b5" kind="소설">
    <title>사랑과 전쟁</title>
    <author>이사랑</author>
    <publisher>전쟁문화사</publisher>
    <price>15000</price>
  </book>

  <book id="b6" kind="수필">
    <title>사랑을 느끼세요</title>
    <author>이사랑</author>
    <publisher>사랑출판사</publisher>
    <price>15000</price>
  </book>

  <book id="b7" kind="잡지">
    <title>월간 중앙</title>
    <author>중앙일보기자</author>
    <publisher>중앙일보사</publisher>
    <price>18000</price>
  </book>

  <book id="b8" kind="잡지">
    <title>주간 경제지</title>
    <author>조선일보기자</author>
    <publisher>조선일보사</publisher>
    <price>27000</price>
  </book>
</booklist>



- booklist.xsl -

<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>

<xsl:template match='/'>
<html>
<body>
<h2><font color='blue'>Our Book's List</font></h2>
<table border='1' cellspacing='0' width='80%'>
<tr bgcolor='#ffff66'><th>title</th><th>kind</th><th>author</th><th>publisher</th><th>price</th></tr>

<xsl:apply-templates select='booklist/book'>
<xsl:sort select='price' data-type='number' order='ascending' />
</xsl:apply-templates>
<!--xsl:apply-templates select='booklist/book[@kind="잡지"]' mode='b'/-->
</table>
<p><xsl:call-template name='company'/></p>
</body></html>
</xsl:template>

<xsl:template match='//book'>
<tr>
<td><font color=''><xsl:value-of select='title'/></font></td>
<td><font color=''><xsl:value-of select='@kind'/></font></td>
<td><font color=''><xsl:value-of select='author'/></font></td>
<td><font color=''><xsl:value-of select='publisher'/></font></td>
<td><font color=''><xsl:value-of select='price'/></font></td>
</tr>
</xsl:template>
<!--
<xsl:template match='//book' mode='b'>
<tr>
<td><font color='red'><xsl:value-of select='title'/></font></td>
<td><font color='red'><xsl:value-of select='author'/></font></td>
</tr>
</xsl:template> -->
<xsl:template name='company'>
<font color='blue'>옥동자 소프트웨어</font>
</xsl:template>
</xsl:stylesheet>


10rwebp #3

2007. 4. 19. 20:08

수정판 (12 : 20)
- WEB306과는 다름



- Employees2.xml (04/16) -

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="employees.xsl"?>
<employees>
 <employee id="123">
  <name>Munson</name>
  <salary payperiod="weekly">23500</salary>
  <department>Development
   <title>Developer</title>
  </department>
 </employee>
 <employee id="124">
  <name>Brown</name>
  <salary payperiod="weekly">51000</salary>
  <department>Human Resources
   <title>Recruiter</title>
  </department>
 </employee>
 <employee id="125">
  <name>Philips</name>
  <salary payperiod="bi-weekly">45000</salary>
  <department>Development
   <title>Tester</title>
  </department>
 </employee>
 <employee id="126">
  <name>Smith</name>
  <salary payperiod="monthly">72000</salary>
  <department>User Education
   <title>Editor</title>
  </department>
 </employee>
</employees>



- employees.xsl (04/16) -
made by K05078 (quoted by K05076, K05081, K05088, K05094, L05133, J05141, J05143, J05144, H05150, etc.)

<?xml version='1.0'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<!-- http://10rwebd.tistory.com -->

<xsl:template match='/'>
<html>
<body>
<table>
<tr><th>ID</th><th>name</th><th>salary</th><th>department</th><th>title</th><th>tax</th></tr>
<xsl:apply-templates select='employees/employee'>
<!-- xsl:sort select='salary' data-type='number' order='descending'/ -->
</xsl:apply-templates>
<!--xsl:apply-templates select='employees/employee[@id="125"]' mode='sin90'/-->
</table>
</body> </html>
</xsl:template>
<xsl:template match='//employee'>
<tr>
<td><xsl:value-of select='@id'/></td>
<td><xsl:value-of select='name'/></td>
<td><xsl:value-of select='salary'/></td>
<td><xsl:value-of select='department'/></td>
<td><xsl:value-of select='department/title'/></td>
<td>
<xsl:choose>
<xsl:when test='salary &lt;= 20000'>low tax</xsl:when>
<xsl:when test='salary &gt;= 50000'>high tax</xsl:when>
<xsl:otherwise>normal tax</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>



- Result -

ID name salary department title tax
123 Munson 23500 Development Developer Developer normal tax
124 Brown 51000 Human Resources Recruiter Recruiter high tax
125 Philips 45000 Development Tester Tester normal tax
126 Smith 72000 User Education Editor Editor high tax



- 상세 설명 -

<xsl:template match='/'> ... </xsl:template>
: template 중 가장 상위의 것으로, match가 / 이라는 것은 전체를 의미한다.
<html> <body> 등, 세부적인 내용이 아닌, 보통 처음 또는 끝에 정의되어야 할 부분이 들어간다. (head와 foot 등)
다른 template를 불러오는 데에는 <xsl:apply-templates select='A/B'> ... </xsl:apply-templates> 를 사용하는데, 이는 함수 호출과 비슷하다. xsl:apply-templates 태그 안에 <xsl:sort select='정렬할 속성명' type='어떤 형태인가(number, text 등)' order='오름차순(ascending) 정렬인지 내림차순(descending) 정렬인지' /> 를 넣으면 select에서 지정해준 속성에 따라 전체를 정렬하여 준다.
apply-templates에서 select 안에는 XML 파일에서 정의한 태그의 이름을 쓰는데, A/B 란 최상위 태그가 A이고 그 바로 아래 태그가 B라는 뜻이다.

xsl:template와 xsl:apply-templates에는 mode라는 속성을 넣을 수 있는데, 같은 match를 가진 템플릿이 여러 개 있는 경우 mode를 사용하여 선택할 수 있다.

match가 'A/B[@C="D"]' 로 써진 경우 A 최상위 태그 아래의 B 태그의 속성 C가 D인 내용만 불러오는 기능을 가지고 있다. D에 씌여진 따옴표는 그 밖의 따옴표와 겹쳐서는 안된다. '를 사용했을 경우 ", "를 사용했을 경우 '로 써야 겹치지 않는다.


<xsl:template match='//B'> ... </xsl:template>
: XML 파일에서 입력한 내용을 출력하기 위한 template이다. match에는 최상위 태그 A를 생략하고 //B 식으로 써 준다.
XML 파일에 입력되어 있는 내용을 출력할 때는 <xsl:value-of select='C'> 식으로 써 준다. 여기서 C는 B 태그 아래에 있는 하위 태그로, XML 파일에 보면 <C> 내용 </C> 식으로 나와 있는데, 여기에서 '내용'을 가져온다.
만약 XML 문서에 <B id='D'> 라고 되어 있을 때 D를 가져오고 싶을 때에는, '@id' 라고 써 주면 된다. 만약 B 하위의 <C id='E'> 라고 되어 있는 경우, 'C/@id' 라고 쓰면 된다.

(조건문)
<xsl:if test='C &lt; n'> F </xsl:if>
이 태그를 C언어로 옮기면 다음과 같다.

if(C < n) { F }

위의 태그에서 &lt;는 '<' 를, &gt;는 '>' 를 의미한다. 이렇게 쓰는 이유는 <나 >를 그대로 썼을 경우 태그의 시작/종료와 혼동이 되기 때문이다.

<xsl:choose>
<xsl:when test='C &lt; n1'> G </xsl:when>
<xsl:when test='C &gt n2'> H </xsl:when>
<xsl:when test='C = n3'> I </xsl:when>
<xsl:otherwise> I </xsl:otherwise>
</xsl:choose>
이 태그를 C언어로 옮기면 다음과 같다.

if(C < n1) { G }
else if (C > n2) { H }
else if (C == n3) { I }
else { J }