http://mocamilk.tistory.com/76#footer 발췌.

DLL 함수 호출 시 발생하는 PInvokeStackImbalance Exception


오늘 회사에서 아는 형이 코드에서 "PInvokeStackImbalance" Exception이 발생한다고 도와달라고 했다.

사실 여기서 가장 큰 문제는 "왜 되던 코드가 안되는 것일까?" 였다. 이전 프로젝트에서는 DLL을 잘 로드해서 썼는데, 새로 프로젝트를 만들고 똑같이 DLL을 로드했는데 갑자기 위의 Exception이 발생했다고 한다.

그래서 두 프로젝트를 비교해봤더니 사용하던 .Net Framework 버전이 달랐다. 잘 되던 곳은 3.0을 쓰고 있었고, 안되는 곳은 4.0을 쓰는 문제가 있었다.

그래서 찾아봤다. 역시 문제는 CallingConvention 문제.

[DllImport("TestDll.dll")]
public static extern int Test(string text);

위와 같이 작성되었던 것을

[DllImport("TestDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int Test(string text);

이렇게 뒤에 CallingConvention을 붙여서 호출하면 된다.


보통 C++에서 DLL을 만들면 cdecl 이란 호출규약으로 만든다. 왜냐면 다들 귀찮으니까 별도의 호출규약을 안써준다. 근데, C#에서 DllImportAttributeCallingConvention은 기본값이 stdcall이다. 그래서 에러가 난거다.


호출규약에 대해서 궁금하면 여기! 를 참고하면 좋다.


뭐... 이거야 쉽게 해결했지만 왜 3.0에서는 된걸까?

여러 글을 찾아보다가 결국 해답은 못찾았다. 하지만 StackOverflow에 같은 질문이 올라온 글에 David Hefferman이란 사람이 아래와 같이 적어놓았다.

Don't kid yourself that your code is alright because .net 3.5 doesn't raise this error. The error detection in .net 4 is better which is why you only see the errors there. But your code is broken in all .net versions.

굳이 해석하자면...

"3.5에서 에러가 나지 않았다고 코드에 이상없다고 착각하지 마. 단지 4.0에서 이러한 에러를 잘 찾아내니까 발생한거야. 니 코드는 모든 .net 버전에서 에러야"


음. 명언이다. 에러가 안난건 단지 4.0의 성능이 좋아서다. 내가 봐도 확실히 에러 맞다.

<script src="http://mocamilk.tistory.com/plugin/CallBack_bootstrapper?&src=http://s1.daumcdn.net/cfs.tistory/v/0/blog/plugins/CallBack/callback&id=76&callbackId=mocamilktistorycom768418&destDocId=callbacknestmocamilktistorycom768418&host=http://mocamilk.tistory.com&float=left&random=572"></script>

 

출처 Hello Krinlion <Comming Soon Blog.krinlion.com> | 키린라이온
원문 http://blog.naver.com/krinlion/40131051496

XSL(eXtensible Sytlesheet Language) : XML문서를 다른 문서로 변환하는 규칙을 가지고 있는 언어.

예)

XML문서 --> HTML문서 변환--> IE
XML문서 --> ???.xls문서 변환-->오피스(엑셀)
XML문서 --> ???.wml문서 변환 --> 무선인터넷
XML문서 --> pdf문서 변환
XML문서 --> XSLT(XSL문서) -->XML문서 변환
등등

 

 

 

 

XSL 세가지 구성 파트

XSLT (XSL Transformation )

XML문서의 구조를 다른 구조로 변환시키기 위해 설계된 마크업 언어이다.

1999년 11월 16일에 발표한 XSLT Version 1.0이 최신 버전이다.

XPath ( XML Path Language )

XML문서의 구조를 다른 구조로 변환시키기 위해서 설계된 마크업 언어

(XML문서 -->또다른 구조의 XML문서 변환)

XSL-FO(XSL Formatting Objects)

XML문서를 비 XML문서로 만들기 위해서 설계된 언어이다.

포멧터(Formatter)라는 프로그램을 통해서 XML문서를 비 XML문서로 변환하기 위해 , 먼저 XML

문서는 XSL-FO 마크업 언어로 작성되어야 한다.

 

 

XSL 처리 과정 종류 2가지

-Transformation : xml문서 --> 다른구조xml문서 ( XSL변환기 브라우저에 내장되어 있음(파서 내장) )

예) XML -> HTML(XHTML)문서

XML -> WML(Wireless Markup Language) 문서

XSL 변환기의 결과물은 메모리에서 트리를 이루는 DOM객체들로 생선된다.

 

 

-Formatting : xml문서 --> 비 xml문서 ( 포맷터 )

XML문서를 특정 소프트/하드웨어에 맞는 비 XML문서로 변호나하는 과정을 Formatting 처리 과정이라고 한다.

처리과정

 

 

 

 

XML 문서가 XSL-FO 언어로 작성되어 있지 않으면, XSL 변환기를 거쳐 XSL-FO 문서 구조로 변경하고 Formatting 처리를 진행해야 한다.

 

 

 

 

 

 

출처 Hello Krinlion <Comming Soon Blog.krinlion.com> | 키린라이온
원문 http://blog.naver.com/krinlion/40131010957

1. Xpath(XML Path Language)란?

ㄱ) XML 문서에서 특정엘리먼트나 속성 접근하기 위해 경로를 지정하는 언어
ㄴ) XPath는 XSLT와 XPointer 언어에 사용될 목적으로 설계된 언어.
(XPath 언어 없이는 XSLT언어로 XSL문서를 작성할 수 없다.)
(XPath 는 공식적으로 XSL의 한 부분이다.)
ㄷ) XPath는 XML문서의 특정 부분에 자유자재로 접근할 수 있는 기능을 제공한다.
ㄹ) XML 문서는 트리 구조로 구조화 되어 있기 때문에 XML문서 내의 특정 부분에 접근하기 위해서는
XPath라는 약속된 경로 표기법을 사용해야 한다.
ㅁ) 절대 경로와 상대경로 사용가능.
-절대 경로는 '/'로 시작
-상대 경로는 '.'(현재단계) '..'(상위단계)을 사용.
ㅂ) XPath는 문자열, 숫자 , boolean, 함수도 있다.

 

XPath는 XSLT와 XPointer 언어의 핵심 구성요소 이며,

XML문서의 프로그램 API인 DOM(Document Object Model)에서 노드를 검색할 때 사용된다.

 

 


2. 노드와 노드 셋

노드: 트리구조의 매듭에 해당하는 것

노드셋(노드집합) : 동일 노드들의 집합.

 

[노드 종류]

1. 루트노드(root node)
xml문서 자체를 표현하는 최상위 노드
루트엘리먼트(booklist)의 부모노드
2.엘리먼트 노드(element node) :
엘리먼트를 표현하는 노드
3.속성노드(attribute node) :
속성을 표현하는 노드
(주의) 네임스페이스는 속성 노드로 표현하지 않는다.
4. 텍스트 노드(text node) :
Content 내용 문자 데이터를 표현하는 노드
트리 구조상의 최말단 노드.

5. 프로세싱 지시자 노드(processing instruction node)
프로세싱 지시자를 표현하는 노드
6. 주석 노드(comment node) : 주석을 표현하는 노드

 

 

 

 

 

 

3. Location Path

ㄱ) Location Path는 XML문서의 각 노드들에 대한 위치
를 지정하기 위한 XPath표현식이다.
ㄴ) Location Path는 절대 경로와 상대 경로
ㄷ) 절대 경로 /
예)
/Location Step/Location Step/.../Location Step

ㄹ) 상대 경로 . ..
예)
Location Step/~ /Location Step
./Location Step/~ /Location Step
../Location Step/~ /Location Step

 

 

 

 

 

4. Location Step 작성 문법

형식

Axis::NodeTest[Predicate]
Axis 노드를 찾기 위한 검색방향(축)
NodeTest 찾을 노드 이름
Predicate 필터링을 하기 위한 표현식 기술

 

 

 


Axis(축) 종류

 

 

a) ancestor 축 ( 조상 )
: 기준 노드의 모든 조상
b) ancestor-or-self 축
: 기준노드 + 기준노드의 조상
c) parent 축 : 기준노드의 부모
d) self 축 : 기준노드
self::node() --> 자기 자신 노드
e) attibute 축 : 기준노드(엘리먼트노드)의 속성노드
f) child축 : 기준 노드의 바로 아래단계의 자식노드만
g) descendant 축 : 기준 노드의 모든 자식노드.
h) descendant-or-self 축 :
기준 노드의 모든 자식노드+기준노드.
i) preceding 축 : 기준노드보다 앞선 모든 노드
j) preceding-sibling 축 :
기준노드보다 앞선 노드 중 형제 노드
k) following 축: 기준 노드보다 뒤에 따라오는 모든 노드
i) following-sibling 축: 기준 노드보다 뒤에 따라오는 형제 노드

 

 

 

 

 

XML문서

<?xml version="1.0" encoding="utf-8" ?>

<booklist>
<book id="b1">
<title>C#</title>
<price>5000</price>
</book>
<book id="b2">
<title>asp.net</title>
<price>3000</price>
</book>
<book id="b3">
<title>java</title>
<price>3500</price>
</book>
<book id="b4">
<title>vb</title>
<price>5000</price>
</book>
</booklist>

 

 

 

 

 

DOM구조

 

 

 

 

 

 

NodeTest에 올 수 있는 것들

(a) 축::엘리먼트명
지정된 축에서 해당 엘리먼트명을 갖는 노드를 선택
(b) 축::속성명
지정된 축에서 해당 속성명을 갖는 노드들을 선택
c) 축::*
지정된 축의 모든 엘리먼트 노드들을 선택
d) 축::접두사:*
지정된 네임스페이스 접두사를 갖는 모든 노드 선택
e)축:접두사:로컬이름
지정된 네임스페이스 접두사를 갖고 로컬이름과 동일한
노드들을 선택
f) 축::node()
지정된 축의 모든 노드(엘리먼트,텍스트 노드 등)들을 선택
g) 축::comment()
지장된 축의 모든 주석 노드들을 선택
h) 축::text()
지정된 축의 모든 텍스트 노드들을 선택
i) 축::processing-instruction
지정된 축의 모든 PI노드들을 선택

 

 

 

 

 

Predicate

a)선택된 노드 중에서 특별한 노드를 선택하기 위한 (조건문,TRUE,FALSE) 표현식
b)-조건문에서 사용될 수 있는 연산자
*,-,div,mod,+,<,>,<=,>=,=,!= ,and,or
c)주로 많이 사용되는 Predicate 표현식
-축::노드명1[child::노드명2="값"]
축방향으로 찾고자하는 노드명1의자식 노드명2 중에 콘텐트(내용)이 "값"인 노드 선택
-축::노드명[attribute::속성명="값"]

-축::노드명[contains(child::노드명="값")]

 

 

 

 

 

5. 단축형 Location Step 작성 문법

a) child::엘리먼트명 ==> 엘리먼트명
b) /child::엘리먼트명/child::엘리먼트명
==>/엘리먼트명/엘리먼트명
c)/child::엘리먼트명[child::엘리먼트명="값"]
==>/엘리먼트명[엘리먼트명="값"]
d) attribute::속성명==>@속성명
e)/child::엘리먼트명/attribute::속성명
==>/엘리먼트명/@속성명
f) desecndant-or-self::엘리먼트명(//최상위 부모노드로부터)
==>//엘리먼트명
g)/child:엘리먼트명/descendant-or-self엘리먼트명
==>/엘리먼트명//엘리먼트명
h)self::node() ==>.
i)parent::node() ==>..
예) ../price
j)/child::엘리먼트명/parent::node()/
==>엘리먼트명/../엘리먼트명

 

 

 

 

 

예제

1)절대경로 표현식, booklist에 자식인 book을 선택
/booklist/book 반드시 booklist 밑에 있는 book밑에 있는 book찾아라.
//book 모든 book 엘리먼트를 찾아라..
지금트리구조에서는 같은 의미이다.
2)절대 경로 표현식 book의 자식 price선택
/booklist/book/price
3)특정 book현재 노드 위치라고 가정하고
자식인 title노드를 찾아라를 상대경로표현식으로
./title
4)현재 title노드가 기준노드라 치고 형제노드인
price를 찾아라를 상대경로표현식으로
../price
5)현재 title노드가 기준노드라 치고 부모노드의 id속성값을
찾아라를 상대경로 표현식으로
../@id

 

 

 

 

 

 

6.Xpath 함수(4종류)

ㄱ)노드정보 반환 함수

number last() : 마지막 노드 위치 반환
/booklist/book[last()]
number position() :현재노드 위치 반환
/booklist/book[position()=2] '='같다의 의미
/booklist/book[position()=last()] 은 /booklist/book[last()]와 같다
number count(node-set):노드 갯수 반환
count(/booklist/book)
node-set id(object) : 고유한 id가지는 노드셋 반환
string local-name(node-set?) : 로컬이름 반환
string name() : QName반환
string namespace-uri() : 접두사 반환

 

 

 

 

 

ㄴ)문자열 관련 함수

string string(object?)
string concat(string,string,string*)
boolean starts-with(string,string)
예)/booklist/book[starts-with(title,'V')]


boolean contains(string,string)
예)/booklist/book[contains(title,'.net')]

string substring-before(string,string)
string substring-after(string,string)
string substring(string,number,number?)
number string-length(string?)
string translate(string,string,string)
string normalize-space(string?) //trim()함수와 같다

 

 

 

 

 

 

ㄷ)참/거짓 반환 함수

boolean not(boolean)
예)/booklist/book/[not(contains(title,'V'))]

 

 

 


ㄹ)숫자관련함수

number(),sum(),floor(),ceiling(),round()

 

윈폼 컨트롤에 XML문서를 읽어와 구조화 시키는 작업을

앞에서도 했고 이 정리도 그 연속이라 보면 되겠다.

 

이번엔 트리뷰이다.

예제에 쓰일 '일기장.xml'

 

이 XML문서를 이렇게 트리뷰에 적용 시킬것이다.

 

 

코딩

private void btnxml파일선택_Click(object sender, EventArgs e)
{
if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
{
//열기 대화상자의 경로 설정
this.pathUri = this.openFileDialog1.FileName;
// 초기화 작업
Initialize();
XmlReader reader;
try
{
reader = this.CreateXmlReader();
if (this.checkBox1.Checked)
{
MessageBox.Show("Validation Document");
}
if (reader.NodeType == XmlNodeType.XmlDeclaration)
{
this.label1.Text=

string.Format( "{0} {1}",reader.Name,reader.Value);
}
//booklist 엘리먼트
while (reader.Read())//시작 엘리먼트가 나올때까지
{
if (reader.IsStartElement()) break;
}
MessageBox.Show(reader.Name);
if (reader.NodeType == XmlNodeType.Element)
{
TreeNode topNode = new TreeNode(reader.Name);
this.treeView1.Nodes.Add(topNode);
// booklist
Nextnode(reader, topNode);
this.treeView1.ExpandAll();
}
}
catch (Exception ex)
{
}
finally
{
}
}

public void Nextnode(XmlReader reader, TreeNode pNode)
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
string id = "";
if (reader.HasAttributes)
{
id = reader.GetAttribute("id");
}
TreeNode cNode = new TreeNode(reader.Name + "(" + id + ")");
pNode.Nodes.Add(cNode);
Nextnode(reader, cNode);
break;
case XmlNodeType.EndElement:
Nextnode(reader, pNode.Parent);
break;
case XmlNodeType.Text:
cNode = new TreeNode(reader.Value);
pNode.Nodes.Add(cNode);
Nextnode(reader, pNode);
break;
}
}
}

 

 

 

DataGrid 컨트롤이든 DataGridView든 바인딩방법은 거의 유사하고 출력결과도 같

으니 시간나면 하나하나 해보시길...

 

굳이 이렇게 바인딩해서 쓸일은 많이 있을 거라 예상은 되지 않는다. 하지만 나중에 결과물 확인창? 뭐

그런걸로 쓰면 유용할 듯...

 

여튼 우리가 만든 문서를 여러 컨트롤의 소스로 이용할 수있다는 것은 참 재밌는 일이다~ ㅎㅎ

 

예제는 우리와 친근한 booklist.xml파일을 이용할 것이다

일단 윈폼에 DataGrid 컨트롤을 하나 던져놓고 버튼도 하나 던져놔 보자

던져놓고 버튼을 더블클릭해 이벤트 핸들러에 아래와 같이 코딩해 보자

private void btn바인딩_Click(object sender, EventArgs e)
{
DataSet ds1 = new DataSet();
ds1.ReadXml(@"..\..\files\booklist.xml");
this.dataGrid1.DataSource = ds1.Tables[0];
}

데이터 셋객체를 생성하고 ReadXml()함수를 이용해 해당 경로에서 xml파일을 읽어들여 ds1(DataSet객체)의 첫번째 테이블로 설정이 되며 이것을 DataGrid의 DataSource로 하면 테이블 형태(200개 행편집모양)로 보여준다.

 

결과 출력

 

이전정리에서는 데이터베이스를 이용 XML관련 문서를 만들어 봤고

이번엔 반대로 XML문서를 이용해 데이터베이스의 테이블 형태로 만들어봤다.

 

예제에 쓰일 booksourcedb데이터 베이스의 book테이블 값들

 

 

 

XML 문서형식으로 만들어 보기

SqlDataAdapter adapter;
DataSet ds;
DataSet tables;
private void Form3_Load(object sender, EventArgs e)
{
string constr = "SERVER=localhost;DATABASE=

booksourcedb;UID=sa;PWD=서버암호";
this.adapter = new SqlDataAdapter("select * from book", constr);
ds = new DataSet("booklist");
this.adapter.Fill(ds, "book");

}

DataSet과 연결시킬 SqlDataAdapter 객체를 만들어 DataSet객체에 데이터 베이스의 'book'테이블 내용을 채워 넣어 초기화 시켰다.

private void GetXml_Click(object sender, EventArgs e)
{
//DataSet-->Xml
string xml = this.ds.GetXml();
Console.WriteLine(xml);
}

GetXml()함수를 사용해서 테이블의 내용을 XML문서 형태로 만들어 주었다. DataSet과 SqlDataAdapter의 개념이해는 ADO.NET에서 2번째 정리를 보면 도움이 될것 같다. 더 정확한 정보는 MSDN에 있으니 참고 하길...

 

 

 

 

결과출력

 

XSD(스키마) 형식으로 만들어 보기

private void GetXmlSchema_Click(object sender, EventArgs e)
{
string schema = this.ds.GetXmlSchema();
Console.WriteLine(schema);
}

데이터셋에있는 테이블을 토대로 스키마 문서 형태로 바꿔 주는 함수 이다.

 

 

 

 

 

 

 

위에 두 출력을 파일로 만들기

파일로 만들때는 함수 이름에 Write가 붙는다.

위에서 쓰인 객체명 그대로 예제로 쓰겠다.

this.ds.WriteXml(@"c:\book.xml");

함수의 인자로 경로를 주면 해당 경로에 해당 문서 형태대로 문서가 생긴다~

this.ds.WriteXmlSchema(@"c:\bookshema.xsd");

 

 

 

결과

 

 

 

 

 

 

 

 

 

XSLT 프로세싱

용어정리

XSLT 프로세싱 : XML문서를 XSL문서와 결합하여 다른 구조의 XML문서로 변환하는 것

Result Tree : XML변환(Transformation) 의 결과물은 파일 형태의 문서가 아니라 메모리 상에서 트리를 이루는 DOM객체를

Result Tree라 한다.

XslCompiledTransform 클래스 : XSLT 변환을 수행하는 클래스 Transform() 메서드 사용.

예제)

xml문서의 내용을 토대로 XSLT프로세싱하여 htm파일로 만들기

xslt문서

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<!--시작 템플릿 룰 선언-->
<xsl:template match="/">
<html>
<body>
<h2>책목록</h2>
<table border="1" cellspacing="0" width="80%">
<tr bgcolor="yellow">
<th>제 목</th>
<th>저 자</th>
<th>가격</th>
</tr>
<xsl:apply-templates select="//book"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<tr align="center">
<td>
<xsl:value-of select="./title"/>
</td>
<td>
<xsl:value-of select="./author"/>
</td>
<td>
<xsl:value-of select="./price"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

 

 

 


코딩

 

예제에 쓰일 xml파일은 늘쓰던 booklist.xml파일이다~ 이전 정리에 여러번 게재했으니 궁금하면 이전 정리를 보면 되겠다.

private void btnXSLT_Click(object sender, EventArgs e)
{

XmlReaderSettings settings=new XmlReaderSettings();
settings.DtdProcessing=DtdProcessing.Parse;
XmlReader reader = XmlReader.Create(pathURI,settings);
// 2)htm문서로 저장할 XmlWirter객체 생성
string path = @"c:\result.htm";
XmlWriter writer = XmlWriter.Create(path);
// 3) XML변환기
// (XslCompiledTransform 클래스사용)
XslCompiledTransform transformer = new XslCompiledTransform();//System.Xml.Xsl
// 변환기가 변환할 xslt 문서지정
transformer .Load(@"..\..\files\booklist.xslt");
// 변환하는 메서드 호출
transformer.Transform(reader , writer);

System.Diagnostics.Process.Start(@"c:\result.htm");

}

XslCompiledTransform클래스 객체를 생성 하여 그 객체명을 transformer 로 하였다.

로 사용될 xslt을 transformer 객체의 Load메서드를 이용해서 셋팅시켜주고 Transform메서드를 이용하여 reader 에 있는 XML 노드 첫번째부터 읽어 XSLT형식으로 변환하여 writer로 파일을 쓴다.

결과출력

htm파일로 만들어졌으며 xslt문서에서 정의한 대로 테이블에 값이 들어가있다.

 

앞선 정리에서 추가,수정을 해봤다.

이제 삭제를 해보겠다. 앞의 내용을 봤다면 삭제도 별것 없다.

예제)

private void btnBook삭제_Click(object sender, EventArgs e)
{
//마지막 Book 엘리먼트를 삭제
XmlDocument doc = new XmlDocument();
doc.Load(pathURI);

XmlElement eBooklist = doc.DocumentElement;
XmlElement eLastBook = eBooklist.LastChild as XmlElement;
//RemoveChild() 삭제 메서드
eBooklist.RemoveChild(eLastBook);
//저장
StringWriter sw = new StringWriter();
doc.Save(sw);
Console.WriteLine(sw.ToString());
sw.Close();
}

RemoveChild()메서드를 이용하여 마지막 자식을 삭제하였다. 이게 끝이다....-_-;;

private void btnBook삭제02_Click(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
doc.Load(pathURI);

XmlElement eBooklist = doc.DocumentElement;
XmlElement eLastBook = eBooklist.LastChild as XmlElement;
//kind 속성 삭제
//eLastBook.RemoveAttributeAt(1);//kind
eLastBook.RemoveAttribute("kind");
//확인
StringWriter sw = new StringWriter();
doc.Save(sw);

Console.WriteLine(sw.ToString());
sw.Close();
}

RemoveAttributeAt()메서드를 이용하여 해당 속성을 삭제 하였다. 이게 끝.... -_-;;;

주석에서 인덱스 값을 넣은 형태의 오버로드도 있는데 첫번째 속성의 인덱스는 0부터이다. kind속성이 2번째 속성이므로 1을 넣거나 바로 아래 코딩처럼 문자열로 속성이름을 직접해줘도 상관 없다.

 

결과출력(엘리먼트 삭제)

마지막 book엘리먼트가 삭제 되어 3개임을 알 수 있다~

 

 

 

결과출력(속성삭제)

마지막 엘리먼트의 kind속성이 삭제 됐음을 알 수 있다.

 

 

 

 

 

XML 문서의 수정은 단순 기존 속성이나 InnerText(컨텐츠) 값을 변경해주는 것이 있고,

엘리먼트를 바꿔주는것도 있다. 엘리먼트를 바꿔 줄때는 엘리먼트의 Name속성자체가

읽기 전용 속성이기 때문에 단순히 변경되지 않는다.

 

먼저 단순 값변경부터 알아 보겠다.

이건 뭐... 정리까지 해야하나 할 정도지만... 일단 간단하니 예제를 보자

 

예제

XmlDocument doc = new XmlDocument();
doc.Load(pathURI);

XmlElement booklist = doc.DocumentElement;
XmlElement fbook= booklist.FirstChild as XmlElement;
//책 제목 , 책 종류 수정
XmlElement fbookTitle= fbook.FirstChild as XmlElement;
//1) 제목 수정 innerText 속성 사용
fbookTitle.InnerText = "ASP";
//2) 종류 수정 : SetAttribute() 메서드 사용
fbook.SetAttribute("kind", "프로그램언어");
//저장
StringWriter sw = new StringWriter();
doc.Save(sw);

Console.WriteLine(sw.ToString());
sw.Close();

예제에서는 booklist의 첫번째 자식 book을 가져와서 그 book엘리먼트의 속성과 컨텐츠 내용을 바꾸는 예제 이다.

새로운 속성이나 메서드는 없다.

 

주석을 보면 알수있겠다. 예상대로 별거 없다. 가져온 엘리먼트의 InnerText속성을 사용해서 값을 다른 값으로 바꿔주고 SetAttribute()메서드를 이용해 해당 속성의 값을 다른 값으로 변경해 주었다. 바로 이전 정리에서 이 메서드는 새로운 엘리먼트의 새로운 속성을 만들어 줄 때도 사용하였다. 이처럼 기존에 있는 속성이름으로 다시 값을 셋팅해주면 덮어쓰기같이 값이 덮어써지기 때문에 마치 수정한것과 같은 결과를 가져온다.

콘솔창에 출력하는 방법인데 파일로 저장하는 방법은 앞에서 정리했으니 파일로 저장하고 싶으면 앞정리를 보면 되겠다.

 

 

 

 

결과출력

 

 

 

XmlDocument doc = new XmlDocument();
doc.Load(pathURI);

XmlElement eBooklist = doc.DocumentElement;
//마지막 book 엘리먼트를 eOldBook으로 저장.
XmlElement eOldBook = eBooklist.LastChild as XmlElement;
// 새로운 book엘리먼트 생성
XmlElement eNewBook = doc.CreateElement("책");
// Book 이라는 엘리먼트의 속성/자식 엘리먼트 추가
// 속성 복사
foreach (XmlAttribute attri in eOldBook.Attributes)
{
eNewBook.SetAttribute(attri.Name, attri.Value);
}
// 자식 복사
foreach (XmlElement eChild in eOldBook.ChildNodes)
{
eNewBook.AppendChild(eChild.CloneNode(true));
//true는 자식의 자식까지 복사하는것 false는 바로 밑에 자식들만 복사 하겠다.
}
//교체
eBooklist.ReplaceChild(eNewBook, eOldBook);
//
//저장
StringWriter sw = new StringWriter();
doc.Save(sw);//콘솔창에 결과 보기 위해 스트림형태로....
Console.WriteLine(sw.ToString());
sw.Close();

이 예제에서는 엘리먼트의 이름을 'book'에서 '책'으로 변경하려 한다. 하지만 앞에서 언급한 것과 같이 Name 속성 자체가 읽기 전용이므로 그렇게 되지 않는다. 그래서 새엘리먼트를 추가해서 원래 엘리먼트의 자식 노드들을 모두 복사해서 기존 엘리먼트와 새 엘리먼트를 교체해야한다.

방법)

먼저 수정이 될 대상을 booklist의 마지막 자식 book으로 정하고 eOldBook에 담아두었다. 그리고 새엘리먼트 이름을 '책'이라 하고 이름을 eNewBook이라 하였다. 속성을 복사에는 자주 봤던 SetAttribute()를 이용해 복사하였고(실은 덮어쓰기) 자식들을 복사할때는 CloneNode()메서드를이용 하였는데 매개변수 타입이 boolean형이다. true를 할 경우 깊숙한 곳의 자식까지 노드가 이동해서 해당 노드를 XmlNode타입으로 반환할 것이고, false로 하면 바로 밑의 자식들로만 접근하여 복사할 것이다.

모든 작업이 끝난후 ReplaceChild()메서드를 이용해서 교체를 한다.

 

결과출력

마지막 booklist의 4번째 자식 엘리먼트가 '책'으로 바뀐것을 확인 할 수 있다.

 

 

 

 

 

 

XML문서 추가하기

 

XML문서를 DOM객체(메모리에 캐쉬된XML문서)로 만든 후 추가 할때는

정말 새로운 내용만 추가하는 것이 아니라 전체를 다시 덮어 쓴다고 보면 된다.

이것이 가능한 이유는 메모리에 캐쉬 되어있기 때문이다. 메모리에 문서 구조대로

캐쉬가 되어있으니 그 DOM객체에 추가를 하면 메모리에 추가한것이 되고

결국 메모리에 적재된 대로 다시 덮어써주기만 하면 마치 추가된것 처럼 문서가

새로 작성 된다.

 

아래 예제에서 책제목이 'XML 프로그래밍'이라는 새로운 노드를 추가해 보겠다.

구분짓기 위해 색깔을 다양하게 줬다.

XmlDocument doc = new XmlDocument();
doc.Load(pathURI);

// 최상위 엘리먼트 : booklist
XmlElement ebooklist = doc.DocumentElement;
// 새로운 book 엘리먼트 생성
XmlElement eBook= doc.CreateElement("book");
//1.id,kind속성 추가
eBook.SetAttribute("id", "b005");
eBook.SetAttribute("kind", "컴퓨터");
//2.title 엘리먼트를 eBook 추가
XmlElement eTitle= doc.CreateElement("title");
XmlText txtTitle= doc.CreateTextNode("XML 프로그래밍");
eTitle.AppendChild(txtTitle);
eBook.AppendChild(eTitle);
//3. author 엘리먼트를 eBook 추가
XmlElement eAuthor = doc.CreateElement("author");
XmlText txtAuthor= doc.CreateTextNode("장동건이");
eAuthor .AppendChild(txtAuthor);
eBook.AppendChild(eAuthor );
//4. price 엘리먼트를 eBook에 추가
XmlElement ePrice = doc.CreateElement("price");
ePrice .SetAttribute("unit", "원");
XmlText txtPrice = doc.CreateTextNode("20000");
ePrice .AppendChild(txtPrice);
eBook.AppendChild(ePrice);
//엘리먼트추가
ebooklist.AppendChild(eBook);
//저장
//System.IO추가
StringWriter sw=new StringWriter();
doc.Save(sw);//콘솔창에 결과 보기 위해 스트림형태로....
Console.WriteLine(sw.ToString());
sw.Close();

ebooklist : 루트 엘리먼트를 booklist변수에 담았다.

SetAttribute함수를 이용해문서(doc)객체에 'book'이라는 이름의 객체를 만들었다.

'book'엘리먼트에 id,kind 속성을 주고 각각 값을 b005,컴퓨터 로 설정했다.

'title'이라는 이름으로 doc(DOM객체)에 엘리먼트를 만들어 그 객체를 eTitle에 담았다.

'eTitle'의 텍스트노드(내용(컨텐츠))를 만들기 위해 CreateTextNode()함수를 이용하였다. 만들어진 텍스트 노드를 'eTitle'의 자식으로 넣어주기 위해 AppendChild()함수를 사용했다.그렇게 텍스트노드까지 설정된 'eTitle'를 'eBook'의 자식으로 넣어줬다.

아래는 모두 같은 코딩의 반복이다. 엘리먼트를 만들고 자식이 있으면 자식을 만들고 속성이 있으면 속성을 만들고 텍스트가 있으면 텍스트를 만들고 각각 해당하는 엘리먼트의 자식으로만 추가시키면 된다. 끝

여기서 StringWirter를 썼지만 이는 간단히 콘솔창에서 보기 위함이다. 굳이 이렇게 하지 않고 Save()함수 인자로 경로를 문자열로 넣어주면 해당 경로에 파일을 생성할 수 있다.이 외에도 2가지 오버로드가 더 있으니 MSDN을 참고하면 되겠다.

예)

doc.Save(@"c:\booklistTestAdd.xml");

 

출력결과)

 

콘솔,파일 모두 제대로 추가 된것을 확인 할 수 있다

 

+ Recent posts