Làm cách nào để bạn tuần tự hóa một chuỗi dưới dạng CDATA bằng XmlSerializer?


90

Có thể thông qua một thuộc tính nào đó để tuần tự hóa một chuỗi dưới dạng CDATA bằng cách sử dụng .Net XmlSerializer không?


2
Một điều đáng chú ý về hai câu trả lời là bạn không cần CDataContentnếu bạn chỉ đọc XML. XmlSerializer.Deserializesẽ tự động chuyển nó thành văn bản cho bạn.
Chris S

Câu trả lời:


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
Đối với tôi, đây không phải là giải pháp thanh lịch nhất. Đây có phải là cách duy nhất có thể để làm điều này?
jamesaharvey

1
Tôi nghĩ đây là cách duy nhất để đạt được điều này, tôi đã xem chủ đề này ở nơi khác và luôn có cùng một câu trả lời. Ví dụ từ Philip thì gọn gàng hơn một chút nhưng cùng một khái niệm. Cách duy nhất mà tôi biết là triển khai <a href=" msdn.microsoft.com/en-us/library/…> của riêng bạn trên một lớp đại diện cho nội dung CDATA.
csharptest.net

Tôi muốn làm điều tương tự vì có vẻ như việc lưu trữ các chuỗi như CDATA dường như ngụ ý ít thời gian xử lý hơn, vì với nó, chúng tôi có thể 'chỉ' đọc / ghi chuỗi 'như hiện tại'. Liên quan đến các phiên bản XmlDocument / XmlCDataSection đắt như thế nào?
tishma

Và toàn bộ Thuộc tính đều ở đó để chúng ta có thể giữ sạch các lớp mô hình miền khỏi các chi tiết logic tuần tự hóa. Thật buồn nếu con đường bẩn thỉu là cách duy nhất.
tishma

2
Giải pháp của Philip ở phía xa trang hơn một chút là điều cần làm gọn gàng hơn.
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;
        }
    }
}

Sử dụng:

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());

Đầu ra:

<?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>

Điều này vừa tiết kiệm ngày của tôi. Cảm ơn bạn.
Robert

4
// Trong trường hợp bạn cần CDATA trống, bạn có thể đặt mặc định nếu giá trị nguồn là null để tránh ngoại lệ. XmlDocument().CreateCDataSection(MyString ?? String.Empty);
Asereware

@ pr0gg3r có phải điều này cũng cho phép giải kích thước trên cùng một đối tượng không? Tôi đang gặp rắc rối với điều đó
Martin

Làm cách nào để tạo CDATA dưới dạng giá trị văn bản (chứ không phải dưới dạng phần tử) chẳng hạn như <MyClass> <! [CDATA [<test> Hello World </test>]]> </MyClass>?
mko

1
Chỉ cần có khả năng xử lý các giá trị rỗng / null hơn là xuất ra <emptyfield><![CDATA[]]> </emptyfield>
bluee

91

Ngoài cách được đăng bởi John Saunders, bạn có thể sử dụng một XmlCDataSection làm loại trực tiếp, mặc dù nó tổng hợp gần như giống nhau:

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

1
@Philip, cái này có hoạt động với quá trình giải mã không? Tôi đã thấy các ghi chú nói rằng setter sẽ nhận được giá trị XmlText.
John Saunders

1
@John Saunders - Nó thực sự nhận một giá trị XmlCharacterData trong setter trong quá trình giải mã hóa, đó là những gì gọi đến .Value trong setter (ban đầu tôi đặt nó là ToString () từ bộ nhớ, nhưng điều đó không chính xác.)
Philip Rieck

1
@PhilipRieck Còn nếu chúng ta cần quấn một đối tượng tùy chỉnh xung quanh CDataSection. Tạo CDataSection chấp nhận chuỗi.
zeppelin

Cảm ơn bạn! Giải pháp dễ dàng nhất. Hoạt động tốt cho tôi.
Antonio Rodríguez

43

Trong lớp được nối tiếp:

public CData Content { get; set; }

Và lớp 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);
    }
}

Hoạt động như một sự quyến rũ. Cảm ơn bạn.
Leonel Sanches da Silva

3
Câu trả lời này đáng được ghi nhận hơn. Mặc dù, kiểu CData tùy chỉnh không còn có các phương thức tích hợp sẵn tiện lợi mà kiểu System.String thích.
Lionet Chen

Tốt lắm thì câu trả lời đầu tiên
Hsin-Yu Chen

Câu trả lời hoạt động tuyệt vời. Thật tiếc là XmlElement không hoạt động trên trường chuỗi, sau đó bạn chỉ có thể thêm một loại cdata, nhưng bất cứ điều gì ...
jjxtra

Hoàn hảo! Cảm ơn!
Roy

5

Tôi cũng có nhu cầu tương tự nhưng yêu cầu định dạng đầu ra khác - tôi muốn có một thuộc tính trên nút chứa CDATA. Tôi đã lấy một số cảm hứng từ các giải pháp trên để tạo ra giải pháp của riêng mình. Có thể nó sẽ giúp ích cho ai đó trong tương lai ...

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()
    {

    }
}

Trong đối tượng cha được tuần tự hóa, tôi có thuộc tính sau:

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

Tôi nhận được kết quả sau:

<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
Tôi cần phải làm chính xác điều này. Cảm ơn bạn!!
Lews Therin

4

Trong trường hợp của tôi, tôi đang sử dụng các trường hỗn hợp, một số CDATA một số thì không, ít nhất là đối với tôi, giải pháp sau đang hoạt động ....

Bằng cách luôn đọc trường Giá trị, tôi nhận được nội dung, bất kể là CDATA hay chỉ là văn bản thuần túy.

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

    [XmlText]
    public string Value;

Muộn còn hơn không.

Chúc mừng


Thật tuyệt vời - tôi có cảm giác câu trả lời này đã tiết kiệm cho tôi một khoảng thời gian! Để biết thông tin, tôi đã sử dụng thuộc tính [XmlIgnore] trên Value
d219,

Câu trả lời của pr0gg3r về mặt hoạt động khác nhau như thế nào?
ruffin

2

Việc triển khai này có khả năng xử lý CDATA lồng nhau trong chuỗi bạn đang mã hóa (dựa trên câu trả lời gốc của John Saunders).

Ví dụ: giả sử bạn muốn mã hóa chuỗi ký tự sau thành CDATA:

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

Bạn sẽ muốn kết quả đầu ra trông giống như sau:

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

Việc triển khai sau đây sẽ lặp lại chuỗi, chia nhỏ các phiên bản ...]]>...thành ...]]>...và tạo các phần CDATA riêng biệt cho từng chuỗi .

[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;
            }
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.