คุณจัดลำดับสตริงเป็น CDATA โดยใช้ XmlSerializer ได้อย่างไร


94

เป็นไปได้หรือไม่โดยใช้แอตทริบิวต์ของการเรียงลำดับบางอย่างในการทำให้อนุกรมสตริงเป็น CDATA โดยใช้. Net XmlSerializer


2
สิ่งหนึ่งที่ควรสังเกตเกี่ยวกับคำตอบสองข้อคือคุณไม่จำเป็นต้องใช้CDataContentหากคุณอ่าน XML เท่านั้น XmlSerializer.Deserializeจะเปลี่ยนเป็นข้อความให้คุณโดยอัตโนมัติ
Chris S

คำตอบ:


63
[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
}

9
สำหรับฉันสิ่งนี้ดูเหมือนจะไม่ใช่วิธีแก้ปัญหาที่หรูหราที่สุด นี่เป็นวิธีเดียวที่ทำได้หรือไม่?
jamesaharvey

1
ฉันคิดว่านี่เป็นวิธีเดียวที่จะทำสิ่งนี้ให้สำเร็จฉันเคยเห็นหัวข้อนี้จากที่อื่นและคำตอบเดียวกันเสมอ ตัวอย่างจาก Philip สะอาดกว่าเล็กน้อย แต่มีแนวคิดเดียวกัน วิธีเดียวที่ฉันรู้คือใช้ <a href=" msdn.microsoft.com/en-us/library/…>ของคุณเองในคลาสที่แสดงถึงเนื้อหา CDATA
csharptest.net

ฉันต้องการทำสิ่งเดียวกันนี้เพราะดูเหมือนว่าการจัดเก็บสตริงเนื่องจาก CDATA ดูเหมือนจะบ่งบอกถึงเวลาในการประมวลผลน้อยลงเนื่องจากเราสามารถ 'อ่าน / เขียนสตริง' ได้ตามที่เป็นอยู่เท่านั้น การเกี่ยวข้องกับอินสแตนซ์ XmlDocument / XmlCDataSection มีราคาแพงเพียงใด
tishma

และแอตทริบิวต์ทั้งหมดอยู่ที่นั่นเพื่อให้เราสามารถรักษาคลาสโมเดลโดเมนให้สะอาดจากรายละเอียดลอจิกการทำให้เป็นอนุกรม มันน่าเศร้ามากถ้าทางสกปรกเป็นทางเดียว
tishma

2
วิธีแก้ปัญหาของ Philip ที่อยู่ไกลออกไปจากหน้ากระดาษเล็กน้อยเป็นสิ่งที่ต้องทำ
Karl

103
[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 ว่างคุณสามารถตั้งค่าเริ่มต้นได้หากค่าต้นทางเป็นโมฆะเพื่อหลีกเลี่ยงข้อยกเว้น XmlDocument().CreateCDataSection(MyString ?? String.Empty);
Asereware

@ pr0gg3r สิ่งนี้อนุญาตให้ deserializing วัตถุเดียวกันด้วยหรือไม่ ฉันมีปัญหากับเรื่องนั้น
Martin

จะสร้าง CDATA เป็นค่าข้อความได้อย่างไร (ไม่ใช่องค์ประกอบ) เช่น <MyClass> <! [CDATA [<test> Hello World </test>]]> </MyClass>
mko

1
จำเป็นต้องสามารถจัดการกับค่าว่าง / ค่าว่างได้เท่านั้นกว่าการแสดงผล <emptyfield><![CDATA[]]> </emptyfield>
bluee

92

นอกเหนือจากวิธีที่โพสต์โดย 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
@ ฟิลิปมันใช้ได้กับ deserialization หรือเปล่า? ฉันเคยเห็นบันทึกบอกว่า setter จะได้รับค่า XmlText
John Saunders

1
@ จอห์นแซนเดอร์ - จริงๆแล้วมันได้รับค่า XmlCharacterData ใน setter ระหว่าง deserialization ซึ่งเป็นสิ่งที่เรียกไปยังค่าสำหรับใน setter (เดิมทีฉันใช้เป็น ToString () จากหน่วยความจำ แต่มันไม่ถูกต้อง)
Philip Rieck

1
@PhilipRieck แล้วถ้าเราต้องการห่อวัตถุที่กำหนดเองรอบ CDataSection สร้าง CDataSection ยอมรับสตริง
zeppelin

ขอขอบคุณ! ทางออกที่ง่ายที่สุด ใช้ได้ดีสำหรับฉัน
Antonio Rodríguez

44

ในคลาสที่จะทำให้เป็นอนุกรม:

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

คำตอบแรกก็ดี
Hsin-Yu Chen

คำตอบใช้งานได้ดี เป็นเรื่องน่าเสียดายที่ XmlElement ไม่ทำงานในฟิลด์สตริงคุณสามารถเพิ่มประเภท cdata ได้ แต่อะไรก็ได้ ...
jjxtra

สมบูรณ์แบบ! ขอบคุณ!
รอย

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

4

ในกรณีของฉันฉันใช้ฟิลด์ผสม CDATA บางตัวก็ไม่ใช่อย่างน้อยสำหรับฉันวิธีแก้ปัญหาต่อไปนี้ใช้งานได้ ...

เมื่ออ่านช่อง Value เสมอฉันจะได้รับเนื้อหาไม่ว่าจะเป็น CDATA หรือข้อความธรรมดาก็ตาม

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

    [XmlText]
    public string Value;

มาสายดีกว่าไม่มาเลย.

ไชโย


เยี่ยมมาก - ฉันรู้สึกว่าคำตอบนี้ช่วยให้ฉันประหยัดเวลาได้มาก! สำหรับข้อมูลฉันใช้แอตทริบิวต์ [XmlIgnore] กับ 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.