XmlSerializer를 사용하여 문자열을 CDATA로 직렬화하는 방법은 무엇입니까?


90

.Net XmlSerializer를 사용하여 문자열을 CDATA로 직렬화하는 일종의 속성을 통해 가능합니까?


2
두 가지 답변에 대해 주목할 가치가있는 한 가지는 CDataContentXML 만 읽는 경우 에는 필요하지 않다는 것 입니다. XmlSerializer.Deserialize자동으로 텍스트로 변환됩니다.
Chris S

답변:


62
[XmlRoot("root")]
public class Sample1Xml
{
    internal Sample1Xml()
    {
    }

    [XmlElement("node")]
    public NodeType Node { get; set; }

    #region Nested type: NodeType

    public class NodeType
    {
        [XmlAttribute("attr1")]
        public string Attr1 { get; set; }

        [XmlAttribute("attr2")]
        public string Attr2 { get; set; }

        [XmlIgnore]
        public string Content { get; set; }

        [XmlText]
        public XmlNode[] CDataContent
        {
            get
            {
                var dummy = new XmlDocument();
                return new XmlNode[] {dummy.CreateCDataSection(Content)};
            }
            set
            {
                if (value == null)
                {
                    Content = null;
                    return;
                }

                if (value.Length != 1)
                {
                    throw new InvalidOperationException(
                        String.Format(
                            "Invalid array length {0}", value.Length));
                }

                Content = value[0].Value;
            }
        }
    }

    #endregion
}

8
나에게 이것은 가장 우아한 해결책처럼 보이지 않습니다. 이것이 가능한 유일한 방법입니까?
jamesaharvey

1
나는 이것이 이것을 달성하는 유일한 방법이라고 생각하며, 나는이 주제를 다른 곳에서 보았고 항상 같은 대답을 보았습니다. Philip의 예는 좀 더 깔끔하지만 동일한 개념입니다. 내가 아는 유일한 다른 방법 은 CDATA 콘텐츠를 나타내는 클래스에 자신의 <a href=" msdn.microsoft.com/en-us/library/…> 를 구현하는 것 입니다.
csharptest.net

CDATA가 문자열을 '있는 그대로'읽기 / 쓰기 만 할 수 있기 때문에 처리 시간이 줄어드는 것처럼 보이기 때문에 동일한 작업을하고 싶었습니다. XmlDocument / XmlCDataSection 인스턴스와 관련된 비용은 얼마나됩니까?
tishma

그리고 속성 전체가 거기에 있으므로 직렬화 논리 세부 사항에서 도메인 모델 클래스를 깨끗하게 유지할 수 있습니다. 더러운 길이 유일한 길이라면 너무 슬프다.
tishma

2
필립의 솔루션은 페이지에서 약간 아래로 내려가는 것이 더 깔끔합니다.
Karl

99
[Serializable]
public class MyClass
{
    public MyClass() { }

    [XmlIgnore]
    public string MyString { get; set; }
    [XmlElement("MyString")]
    public System.Xml.XmlCDataSection MyStringCDATA
    {
        get
        {
            return new System.Xml.XmlDocument().CreateCDataSection(MyString);
        }
        set
        {
            MyString = value.Value;
        }
    }
}

용법:

MyClass mc = new MyClass();
mc.MyString = "<test>Hello World</test>";
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, mc);
Console.WriteLine(writer.ToString());

산출:

<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MyString><![CDATA[<test>Hello World</test>]]></MyString>
</MyClass>

이것은 내 하루를 구했습니다. 감사합니다.
Robert

4
// 빈 CDATA가 필요한 경우 소스 값이 null이면 예외를 피하기 위해 기본값을 설정할 수 있습니다. XmlDocument().CreateCDataSection(MyString ?? String.Empty);
Asereware 2014

@ pr0gg3r 이것은 동일한 객체에 대한 역 직렬화도 허용합니까? 나는 그것에 문제가 있습니다
Martin

<MyClass> <! [CDATA [<test> Hello World </ test>]]> </ MyClass>와 같은 텍스트 값 (요소가 아닌)으로 CDATA를 만드는 방법은 무엇입니까?
mko

1
<emptyfield> <! [CDATA []]> </ emptyfield>를 출력하는 것보다 비어 있거나 널 (null) 값만 처리 할 수 ​​있어야합니다
bluee

91

John Saunders가 게시 한 방식 외에도 XmlCDataSection 을 형식으로 직접 사용할 수 있지만 거의 동일한 내용으로 요약됩니다.

private string _message;
[XmlElement("CDataElement")]
public XmlCDataSection Message
{  
    get 
    { 
        XmlDocument doc = new XmlDocument();
        return doc.CreateCDataSection( _message);
    }
    set
    {
        _message = value.Value;
    }
}

1
@Philip, 역 직렬화에 작동합니까? setter가 XmlText 값을 받게된다는 메모를 보았습니다.
John Saunders

1
@John Saunders-실제로 deserialization 중에 setter에서 XmlCharacterData 값을받습니다.이 값은 setter에서 .Value에 대한 호출입니다 (원래 메모리에서 ToString ()으로 사용했지만 올바르지 않았습니다.)
Philip Rieck

1
@PhilipRieck CDataSection 주위에 사용자 지정 개체를 래핑해야하는 경우에는 어떻습니까? CDataSection 만들기는 문자열을 허용합니다.
zeppelin

감사합니다! 가장 쉬운 솔루션. 나를 위해 잘 작동합니다.
Antonio Rodríguez

43

직렬화 할 클래스에서 :

public CData Content { get; set; }

그리고 CData 클래스 :

public class CData : IXmlSerializable
{
    private string _value;

    /// <summary>
    /// Allow direct assignment from string:
    /// CData cdata = "abc";
    /// </summary>
    /// <param name="value">The string being cast to CData.</param>
    /// <returns>A CData object</returns>
    public static implicit operator CData(string value)
    {
        return new CData(value);
    }

    /// <summary>
    /// Allow direct assignment to string:
    /// string str = cdata;
    /// </summary>
    /// <param name="cdata">The CData being cast to a string</param>
    /// <returns>A string representation of the CData object</returns>
    public static implicit operator string(CData cdata)
    {
        return cdata._value;
    }

    public CData() : this(string.Empty)
    {
    }

    public CData(string value)
    {
        _value = value;
    }

    public override string ToString()
    {
        return _value;
    }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        _value = reader.ReadElementString();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteCData(_value);
    }
}

매력처럼 작동합니다. 감사합니다.
Leonel Sanches da Silva

3
이 답변은 더 많은 인정을받을 만합니다. 그러나 사용자 지정 CData 형식에는 System.String 형식에서 사용하는 편리한 기본 제공 메서드가 더 이상 없습니다.
Lionet Chen 2017 년

좋은 먼저 대답
Hsin의 - 유 첸

답변이 훌륭합니다. XmlElement가 문자열 필드에서 작동하지 않는 것이
아쉽습니다. 그러면

완전한! 감사!
로이

5

비슷한 요구 사항이 있었지만 다른 출력 형식이 필요했습니다. CDATA가 포함 된 노드에 속성이 필요했습니다. 나는 위의 솔루션에서 영감을 얻어 내 자신을 만들었습니다. 어쩌면 그것은 미래에 누군가를 도울 것입니다 ...

public class EmbedScript
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlText]
    public XmlNode[] Script { get; set; }

    public EmbedScript(string type, string script)
    {
        Type = type;
        Script = new XmlNode[] { new XmlDocument().CreateCDataSection(script) };
    }

    public EmbedScript()
    {

    }
}

직렬화 할 부모 개체에는 다음 속성이 있습니다.

    [XmlArray("embedScripts")]
    [XmlArrayItem("embedScript")]
    public List<EmbedScript> EmbedScripts { get; set; }

다음 출력을 얻습니다.

<embedScripts>
    <embedScript type="Desktop Iframe">
        <![CDATA[<div id="play_game"><iframe height="100%" src="http://www.myurl.com" width="100%"></iframe></div>]]>
    </embedScript>
    <embedScript type="JavaScript">
        <![CDATA[]]>
    </embedScript>
</embedScripts>

1
나는 이것을 정확히해야했다. 감사합니다!!
Lews Therin 2018

4

제 경우에는 혼합 필드를 사용하고 있으며 일부 CDATA는 그렇지 않습니다. 적어도 다음 솔루션이 작동합니다 ....

항상 Value 필드를 읽으면 CDATA이든 일반 텍스트이든 상관없이 내용을 얻습니다.

    [XmlElement("")]
    public XmlCDataSection CDataValue {
        get {
            return new XmlDocument().CreateCDataSection(this.Value);
        }
        set {
            this.Value = value.Value;
        }
    }

    [XmlText]
    public string Value;

안하는 것보다 늦게하는 것이 낫다.

건배


환상적입니다.이 답변을 통해 시간을 절약 할 수 있다고 생각합니다! 정보를 위해 Value
d219

이것이 pr0gg3r의 답변 과 운영상 어떻게 다른 가요?
ruffin

2

이 구현에는 인코딩중인 문자열 내에서 중첩 된 CDATA를 처리 할 수있는 기능이 있습니다 (John Saunders 원래 답변 기반).

예를 들어 다음 리터럴 문자열을 CDATA로 인코딩한다고 가정합니다.

I am purposefully putting some <![CDATA[ cdata markers right ]]> in here!!

결과 출력은 다음과 같이 표시됩니다.

<![CDATA[I am purposefully putting some <![CDATA[ cdata markers right ]]]]><![CDATA[> in here!!]]>

문자열에 비해 다음과 같은 구현 의지 루프의 인스턴스를 분할 ...]]>......]]하고 >...각각에 대해 별도의 CDATA 섹션을 만들 수 있습니다.

[XmlRoot("root")]
public class Sample1Xml
{
    internal Sample1Xml()
    {
    }

    [XmlElement("node")]
    public NodeType Node { get; set; }

    #region Nested type: NodeType

    public class NodeType
    {
        [XmlAttribute("attr1")]
        public string Attr1 { get; set; }

        [XmlAttribute("attr2")]
        public string Attr2 { get; set; }

        [XmlIgnore]
        public string Content { get; set; }

        [XmlText]
        public XmlNode[] CDataContent
        {
            get
            {
                XmlDocument dummy = new XmlDocument();
                List<XmlNode> xmlNodes = new List<XmlNode>();
                int tokenCount = 0;
                int prevSplit = 0;
                for (int i = 0; i < Content.Length; i++)
                {
                    char c = Content[i];
                    //If the current character is > and it was preceded by ]] (i.e. the last 3 characters were ]]>)
                    if (c == '>' && tokenCount >= 2)
                    {
                        //Put everything up to this point in a new CData Section
                        string thisSection = Content.Substring(prevSplit, i - prevSplit);
                        xmlNodes.Add(dummy.CreateCDataSection(thisSection));
                        prevSplit = i;
                    }
                    if (c == ']')
                    {
                        tokenCount++;
                    }
                    else
                    {
                        tokenCount = 0;
                    }
                }
                //Put the final part of the string into a CData section
                string finalSection = Content.Substring(prevSplit, Content.Length - prevSplit);
                xmlNodes.Add(dummy.CreateCDataSection(finalSection));

                return xmlNodes.ToArray();
            }
            set
            {
                if (value == null)
                {
                    Content = null;
                    return;
                }

                if (value.Length != 1)
                {
                    throw new InvalidOperationException(
                        String.Format(
                            "Invalid array length {0}", value.Length));
                }

                Content = value[0].Value;
            }
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.