XML 내용을 작성할 때 특수 문자 변환

업데이트: November 2007

XmlWriter에는 원시 태그를 직접 작성할 수 있도록 하는 WriteRaw 메서드가 포함되어 있습니다. 이 메서드는 특수 문자가 이스케이프되지 않도록 합니다. 이와 대조적으로, WriteString 메서드는 일부 문자열을 동등한 엔터티 참조로 이스케이프합니다. 이스케이프되는 문자는 XML(Extensible Markup Language) 1.0(Second Edition) 권장 사항의 2.4 Character Data and Markup3.3.3 Attribute-Value Normalization 단원에 나와 있는 XML 1.0 권장 사항에 명시되어 있습니다. 특성 값을 작성할 때 WriteString 메서드를 호출하면 '와 "가 이스케이프됩니다. 공백 문자 0x9, 0x10 및 0x13을 제외한 문자 값 0x-0x1F는 �부터 까지의 숫자 엔터티로 인코딩됩니다.

따라서 WriteStringWritingRaw의 사용 시기에 대한 기준은 다음과 같습니다. WriteRaw는 정확히 주어진 항목만 작성하므로, 엔터티 문자를 찾기 위해 모든 문자를 확인해야 할 때는 WriteString을 사용합니다.

WriteNode 메서드는 현재 노드에서 모든 항목을 복사하고 판독기는 작성기로 위치가 지정됩니다. 그러면 판독기는 다음 형제 노드로 이동하여 추가로 처리됩니다. writeNode 메서드를 사용하면 문서 간 정보를 신속하게 추출할 수 있습니다.

다음 표에서는 WriteNode 메서드에 지원되는 노드 형식을 보여 줍니다.

노드 형식

설명

Element

요소 노드와 모든 특성 노드를 작성합니다.

Attribute

작동하지 않습니다. WriteStartAttribute 또는 WriteAttributeString을 사용하여 특성을 작성합니다.

Text

텍스트 노드를 작성합니다.

CDATA

CDATA 섹션 노드를 작성합니다.

EntityReference

Entity Ref 노드를 작성합니다.

ProcessingInstruction

PI 노드를 작성합니다.

Comment

Comment 노드를 작성합니다.

DocumentType

DocType 노드를 작성합니다.

Whitespace

Whitespace 노드를 작성합니다.

SignificantWhitespace

Whitespace 노드를 작성합니다.

EndElement

작동하지 않습니다.

EndEntity

작동하지 않습니다.

다음 예제에서는 "<" 문자를 지정했을 때 WriteString 메서드와 WriteRaw 메서드의 차이를 보여 줍니다. 이 코드 샘플에서는 WriteString을 사용합니다.

w.WriteStartElement("myRoot")
w.WriteString("<")
w.WriteEndElement()
Dim tw As New XmlTextWriter(Console.Out)
tw.WriteDocType(name, pubid, sysid, subset)
w.WriteStartElement("myRoot");
w.WriteString("<");
w.WriteEndElement();
XmlTextWriter tw = new XmlTextWriter(Console.Out);
tw.WriteDocType(name, pubid, sysid, subset);

출력

<myRoot>&lt;</myRoot>

이 코드 샘플에서는 WriteRaw를 사용하며, 출력에 잘못된 문자가 요소 내용으로 포함되어 있습니다.

w.WriteStartElement("myRoot")
w.WriteRaw("<")
w.WriteEndElement()
w.WriteStartElement("myRoot");
w.WriteRaw("<");
w.WriteEndElement();

출력

<myRoot><</myRoot>

다음 예제에서는 XML 문서를 요소 중심 문서에서 특성 중심 문서로 변환하는 방법을 보여 줍니다. XML 특성 중심 문서를 다시 요소 중심 문서로 변환할 수도 있습니다. 요소 중심 모드는 XML 문서가 요소는 많이 포함하지만 특성은 거의 포함하지 않도록 디자인되었다는 것을 의미합니다. 특성 중심 디자인에는 요소가 거의 포함되지 않으며, 요소 중심 디자인에서 요소가 될 항목이 요소의 특성으로 만들어졌습니다. 따라서 요소 개수는 적고 요소당 특성 개수는 많습니다.

이 샘플은 다른 모드로 변환할 수 있으므로 두 모드에서 XML 데이터를 디자인한 경우에 유용합니다.

다음 XML은 요소 중심 문서를 사용합니다. 요소에 특성은 포함되어 있지 않습니다.

입력 - centric.xml

<?xml version='1.0' encoding='UTF-8'?>
<root>
    <Customer>
        <firstname>Jerry</firstname>
        <lastname>Larson</lastname>
        <Order>
        <OrderID>Ord-12345</OrderID>
          <OrderDetail>
            <Quantity>1301</Quantity>
            <UnitPrice>$3000</UnitPrice>
            <ProductName>Computer</ProductName>
          </OrderDetail>
        </Order>
    </Customer>
</root>

다음 샘플 응용 프로그램은 변환을 수행합니다.

' The program will convert an element-centric document to an 
' attribute-centric document or element-centric to attribute-centric.
Imports System
Imports System.Xml
Imports System.IO
Imports System.Text
Imports System.Collections

Class ModeConverter
   Private bufferSize As Integer = 2048
  
   Friend Class ElementNode
      Private _name As [String]
      Private _prefix As [String]
      Private _namespace As [String]
      Private _startElement As Boolean
      
      Friend Sub New()
         Me._name = Nothing
         Me._prefix = Nothing
         Me._namespace = Nothing
         Me._startElement = False
      End Sub 'New
      
      Friend Sub New(prefix As [String], name As [String], [nameSpace] As [String])
         Me._name = name
         Me._prefix = prefix
         Me._namespace = [nameSpace]
      End Sub 'New
      
      Public ReadOnly Property name() As [String]
         Get
            Return _name
         End Get
      End Property
      
      Public ReadOnly Property prefix() As [String]
         Get
            Return _prefix
         End Get
      End Property
      
      Public ReadOnly Property [nameSpace]() As [String]
         Get
            Return _namespace
         End Get
      End Property
      
      Public Property startElement() As Boolean
         Get
            Return _startElement
         End Get
         Set
            _startElement = value
         End Set
      End Property
   End Class 'ElementNode
   
   ' Entry point which delegates to C-style main Private Function.
   Public Overloads Shared Sub Main()
      Main(System.Environment.GetCommandLineArgs())
   End Sub
   
   Overloads Public Shared Sub Main(args() As [String])
      Dim modeConverter As New ModeConverter()
      If args(0) Is Nothing Or args(0) = "?" Or args.Length < 2 Then
         modeConverter.Usage()
         Return
      End If
      Dim sourceFile As New FileStream(args(1), FileMode.Open, FileAccess.Read, FileShare.Read)
      Dim targetFile As New FileStream(args(2), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)
      If args(0) = "-a" Then
         modeConverter.ConertToAttributeCentric(sourceFile, targetFile)
      Else
         modeConverter.ConertToElementCentric(sourceFile, targetFile)
      End If
      Return
   End Sub 'Main
   
   Public Sub Usage()
      Console.WriteLine("? This help message " + ControlChars.Lf)
      Console.WriteLine("Convert -mode sourceFile, targetFile " + ControlChars.Lf)
      Console.WriteLine(ControlChars.Tab + " mode: e element centric" + ControlChars.Lf)
      Console.WriteLine(ControlChars.Tab + " mode: a attribute centric" + ControlChars.Lf)
   End Sub 'Usage
   
   Public Sub ConertToAttributeCentric(sourceFile As FileStream, targetFile As FileStream)
      ' Stack is used to track how many.
      Dim stack As New Stack()
      Dim reader As New XmlTextReader(sourceFile)
      reader.Read()
      Dim writer As New XmlTextWriter(targetFile, reader.Encoding)
      writer.Formatting = Formatting.Indented
      
      Do
         Select Case reader.NodeType
            Case XmlNodeType.XmlDeclaration
               writer.WriteStartDocument((Nothing = reader.GetAttribute("standalone") Or "yes" = reader.GetAttribute("standalone")))
            
            Case XmlNodeType.Element
               Dim element As New ElementNode(reader.Prefix, reader.LocalName, reader.NamespaceURI)
               
               If 0 = stack.Count Then
                  writer.WriteStartElement(element.prefix, element.name, element.nameSpace)
                  element.startElement = True
               End If
               
               stack.Push(element)
            
            Case XmlNodeType.Attribute
                  Throw New Exception("We should never been here!")
            
            Case XmlNodeType.Text
               Dim attribute As New ElementNode()
               attribute = CType(stack.Pop(), ElementNode)
               element = CType(stack.Peek(), ElementNode)
               If Not element.startElement Then
                  writer.WriteStartElement(element.prefix, element.name, element.nameSpace)
                  element.startElement = True
               End If
               writer.WriteStartAttribute(attribute.prefix, attribute.name, attribute.nameSpace)
               writer.WriteRaw(reader.Value)
               reader.Read() 'jump over the EndElement
            
            Case XmlNodeType.EndElement
               writer.WriteEndElement()
               stack.Pop()
            
            
            Case XmlNodeType.CDATA
               writer.WriteCData(reader.Value)
            
            Case XmlNodeType.Comment
               writer.WriteComment(reader.Value)
            
            Case XmlNodeType.ProcessingInstruction
               writer.WriteProcessingInstruction(reader.Name, reader.Value)
            
            Case XmlNodeType.EntityReference
               writer.WriteEntityRef(reader.Name)
            
            Case XmlNodeType.Whitespace
                writer.WriteWhitespace(reader.Value);
            
            Case XmlNodeType.None
               writer.WriteRaw(reader.Value)
            
            Case XmlNodeType.SignificantWhitespace
               writer.WriteWhitespace(reader.Value)
            
            Case XmlNodeType.DocumentType
               writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value)
            
            Case XmlNodeType.EndEntity
            
            
            Case Else
               Console.WriteLine(("UNKNOWN Node Type = " + CInt(reader.NodeType)))
         End Select
      Loop While reader.Read()
      
      writer.WriteEndDocument()
      
      reader.Close()
      writer.Flush()
      writer.Close()
   End Sub 'ConertToAttributeCentric
   
   
   ' Use the WriteNode to simplify the process.
   Public Sub ConertToElementCentric(sourceFile As FileStream, targetFile As FileStream)
      Dim reader As New XmlTextReader(sourceFile)
      reader.Read()
      Dim writer As New XmlTextWriter(targetFile, reader.Encoding)
      writer.Formatting = Formatting.Indented
      Do
         Select Case reader.NodeType
            
            Case XmlNodeType.Element
               writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI)
               If reader.MoveToFirstAttribute() Then
                  Do
                     writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI)
                     writer.WriteRaw(reader.Value)
                     writer.WriteEndElement()
                  Loop While reader.MoveToNextAttribute() 
                  
                  writer.WriteEndElement()
               End If
            
            Case XmlNodeType.Attribute
                  Throw New Exception("We should never been here!")
            
            Case XmlNodeType.Whitespace
               writer.WriteWhitespace(reader.Value)
            
            Case XmlNodeType.EndElement
               writer.WriteEndElement()
            
            Case XmlNodeType.Text
                  Throw New Exception("The input document is not a attribute centric document" + ControlChars.Lf)
            
            Case Else
               Console.WriteLine(reader.NodeType)
               writer.WriteNode(reader, False)
         End Select
      Loop While reader.Read()
      
      reader.Close()
      writer.Flush()
      writer.Close()
   End Sub 'ConertToElementCentric
End Class 'ModeConverter
// The program will convert an element-centric document to an 
// attribute-centric document or element-centric to attribute-centric.
using System;
using System.Xml;
using System.IO;
using System.Text;
using System.Collections;

class ModeConverter {
    private const int bufferSize=2048;

    internal class ElementNode {
        String _name;
        String _prefix;
        String _namespace;
        bool   _startElement;
        internal ElementNode() {
            this._name = null;
            this._prefix = null;
            this._namespace = null;
            this._startElement = false;
        }
        internal ElementNode(String prefix, String name, String nameSpace) {
            this._name = name;
            this._prefix = prefix;
            this._namespace = nameSpace;
        }
        public String name{
            get { return _name;   }
        }
        public String prefix{
            get { return _prefix;   }
        }
        public String nameSpace{
            get { return _namespace;   }
        }
        public bool startElement{
            get { return _startElement;   }
            set { _startElement = value;}
        }
    }
    public static void Main(String[] args) {
        ModeConverter modeConverter = new ModeConverter();
        if (args[0]== null || args[0]== "?" || args.Length < 2 ) {
            modeConverter.Usage();
            return;
        }
        FileStream sourceFile = new FileStream(args[1], FileMode.Open, FileAccess.Read, FileShare.Read);
        FileStream targetFile = new FileStream(args[2], FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
        if (args[0] == "-a") {
            modeConverter.ConertToAttributeCentric(sourceFile, targetFile);
        } else {
            modeConverter.ConertToElementCentric(sourceFile, targetFile);
        }
        return;
    }
    public void Usage() {
        Console.WriteLine("? This help message \n");
        Console.WriteLine("Convert -mode sourceFile, targetFile \n");
        Console.WriteLine("\t mode: e element centric\n");
        Console.WriteLine("\t mode: a attribute centric\n");
    }
    public void ConertToAttributeCentric(FileStream sourceFile, FileStream targetFile) {
        // Stack is used to track how many.
        Stack stack = new Stack();
        XmlTextReader reader = new XmlTextReader(sourceFile);
        reader.Read();
        XmlTextWriter writer = new XmlTextWriter(targetFile, reader.Encoding);
        writer.Formatting = Formatting.Indented;

        do {
            switch (reader.NodeType) {
                case XmlNodeType.XmlDeclaration:
                    writer.WriteStartDocument(null == reader.GetAttribute("standalone") || "yes" == reader.GetAttribute("standalone"));
                    break;

                case XmlNodeType.Element:
                    ElementNode element = new ElementNode(reader.Prefix, reader.LocalName, reader.NamespaceURI);

                    if (0 == stack.Count) {
                        writer.WriteStartElement(element.prefix, element.name, element.nameSpace);
                        element.startElement=true;
                    }

                    stack.Push(element);
                    break;

                case XmlNodeType.Attribute:
                    throw new Exception("We should never been here!");

                case XmlNodeType.Text:
                    ElementNode attribute = new ElementNode();
                    attribute = (ElementNode)stack.Pop();
                    element = (ElementNode)stack.Peek();
                    if (!element.startElement) {
                        writer.WriteStartElement(element.prefix, element.name, element.nameSpace);
                        element.startElement=true;
                    }
                    writer.WriteStartAttribute(attribute.prefix, attribute.name, attribute.nameSpace);
                    writer.WriteRaw(reader.Value);
                    reader.Read(); //jump over the EndElement
                    break;

                case XmlNodeType.EndElement:
                    writer.WriteEndElement();
                    stack.Pop();

                    break;

                case XmlNodeType.CDATA:
                    writer.WriteCData(reader.Value);
                    break;

                case XmlNodeType.Comment:
                    writer.WriteComment(reader.Value);
                    break;

                case XmlNodeType.ProcessingInstruction:
                    writer.WriteProcessingInstruction(reader.Name, reader.Value);
                    break;

                case XmlNodeType.EntityReference:
                    writer.WriteEntityRef( reader.Name);
                    break;

                case XmlNodeType.Whitespace:
                    writer.WriteWhitespace(reader.Value);
                    break;

                case XmlNodeType.None:
                    writer.WriteRaw(reader.Value);
                    break;

                case XmlNodeType.SignificantWhitespace:
                    writer.WriteWhitespace(reader.Value);
                    break;

                case XmlNodeType.DocumentType:
                    writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
                    break;

                case XmlNodeType.EndEntity:
                    break;


                default:
                    Console.WriteLine("UNKNOWN Node Type = " + ((int)reader.NodeType));
                    break;
             }
        } while (reader.Read());

        writer.WriteEndDocument();

        reader.Close();
        writer.Flush();
        writer.Close();
    }

    // Use the WriteNode to simplify the process.
    public void ConertToElementCentric(FileStream sourceFile, FileStream targetFile) {
        XmlTextReader reader = new XmlTextReader(sourceFile);
        reader.Read();
        XmlTextWriter writer = new XmlTextWriter(targetFile, reader.Encoding);
        writer.Formatting = Formatting.Indented;
        do {
            switch (reader.NodeType) {

                case XmlNodeType.Element:
                    writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                    if (reader.MoveToFirstAttribute()) {
                         do {
                            writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                            writer.WriteRaw(reader.Value);
                            writer.WriteEndElement();

                        } while(reader.MoveToNextAttribute());

                        writer.WriteEndElement();
                    }
                    break;

                case XmlNodeType.Attribute:
                     throw new Exception("We should never been here!");

                case XmlNodeType.Whitespace:
                    writer.WriteWhitespace(reader.Value);
                    break;

                case XmlNodeType.EndElement:
                    writer.WriteEndElement();
                    break;

                case XmlNodeType.Text:
                    throw new Exception("The input document is not a attribute centric document\n");

                default:
                    Console.WriteLine(reader.NodeType);
                    writer.WriteNode(reader, false);
                    break;
             }
        } while (reader.Read());

        reader.Close();
        writer.Flush();
        writer.Close();
    }
}

코드를 컴파일한 뒤 명령줄에 **<컴파일된 이름> -a centric.xml <출력 파일 이름>**을 입력하여 실행합니다. 출력 파일은 반드시 있어야 하며 빈 텍스트 파일을 사용할 수 있습니다.

다음 출력의 경우 C# 프로그램이 centric_cs로 컴파일되었다고 가정하여 명령줄에 C:\centric_cs -a centric.xml centric_out.xml을 입력합니다.

-a 모드를 사용하면 응용 프로그램에서 입력 XML이 특성 중심으로 변환되는 반면 -e 모드를 사용하면 요소 중심으로 변경됩니다. 다음 출력은 -a 모드를 사용하여 생성한 새로운 특성 중심의 출력입니다. 여기에는 요소에 중첩 요소 대신 특성이 포함됩니다.

출력: centric_out.xml

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

<root>
<Customer firstname="Jerry" lastname="Larson">
 <Order OrderID="Ord-12345">
  <OrderDetail Quantity="1301" UnitPrice="$3000" ProductName="Computer" />
 </Order>
</Customer>
</root>

참고 항목

개념

XmlTextWriter를 사용하여 제대로 구성된 XML 만들기

XmlTextWriter를 사용하여 XML 출력 서식 지정

XmlTextWriter의 네임스페이스 기능

참조

XmlTextWriter

XmlTextWriter

XmlWriter

XmlWriter