XmlSerializerを使用して文字列をCDATAとしてシリアル化するにはどうすればよいですか?


90

ある種の属性を介して、.Net XmlSerializerを使用して文字列をCDATAとしてシリアル化することは可能ですか?


2
2つの答えについて注意する価値があることの1つは、CDataContentXMLを読み取るだけの場合は必要ないということです。XmlSerializer.Deserialize自動的にテキストに変換されます。
クリス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
これがこれを達成する唯一の方法だと思います。このトピックを他の場所で見たことがありますが、常に同じ答えです。フィリップの例は少しすっきりしていますが、コンセプトは同じです。私が知っている他の唯一の方法は、CDATAコンテンツを表すクラスに独自の<ahref=" msdn.microsoft.com/en-us/library/… > を実装することです
csharptest.net

CDATAは文字列を「現状のまま」で読み取り/書き込みしかできないため、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これにより、同じオブジェクトへの逆シリアル化も可能になりますか?私はそれで問題があります
マーティン

<MyClass> <![CDATA [<test> Hello World </ test>]]> </ MyClass>などのCDATAを(要素としてではなく)テキスト値として作成する方法
mko

1
出力よりもほんの<emptyfield> <![CDATA []]> </ emptyfield>空/ヌル値を処理できるようにする必要があります
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
@フィリップ、これは逆シリアル化に役立ちますか?セッターがXmlText値を受け取るというメモが表示されています。
ジョンサンダース

1
@ジョンサンダース-それは実際.Valueのへの呼び出しがセッターに何のためにあるのかである直列化復元時にセッターにXmlCharacterData値を受け取る(私はもともとのToString()メモリなどからそれを持っていたが、それは間違っていました。)
フィリップRieckを

1
@PhilipRieck CDataSectionの周りにカスタムオブジェクトをラップする必要がある場合はどうでしょうか。Create CDataSectionは文字列を受け入れます。
zeppelin

ありがとうございました!最も簡単なソリューション。私にとってはうまくいきます。
アントニオ・ロドリゲス

42

シリアル化するクラス:

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-Yu Chen

答えはうまくいきます。XmlElementが文字列フィールドで機能しないのは残念ですが、cdataタイプだけを追加できますが、何でも...
jjxtra

パーフェクト!ありがとう!
Roy、

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の[XmlIgnore]属性を使用しました
d219

これはpr0gg3rの回答と操作上どのように異なりますか?
ルフィン

4

同様のニーズがありましたが、異なる出力形式が必要でした。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>

私はこれを正確に行う必要がありました。ありがとうございました!!
Lews Therin

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.