【问题标题】:How do you serialize a string as CDATA using XmlSerializer?如何使用 XmlSerializer 将字符串序列化为 CDATA?
【发布时间】:2021-07-16 20:18:21
【问题描述】:

是否可以通过某种属性使用 .Net XmlSerializer 将字符串序列化为 CDATA?

【问题讨论】:

  • 这两个答案值得注意的一点是,如果您只阅读 XML,则不需要 CDataContentXmlSerializer.Deserialize 会自动为你转成文字。

标签: c# .net xml-serialization


【解决方案1】:
[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>

【讨论】:

  • 这只是节省了我的一天。谢谢。
  • //如果您需要空的 CDATA,您可以设置默认值,如果源值为 null 以避免异常。 XmlDocument().CreateCDataSection(MyString ?? String.Empty);
  • @pr0gg3r 这是否也允许反序列化到同一个对象?我遇到了麻烦
  • 只需要能够处理空/空值而不是输出
  • 最佳解决方案 pr0gg3r
【解决方案2】:

除了 John Saunders 发布的方式之外,您还可以直接使用 XmlCDataSection 作为类型,尽管归结起来几乎相同:

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

【讨论】:

  • @Philip,这对反序列化有用吗?我一直在看到说明 setter 将收到 XmlText 值。
  • @John Saunders - 它实际上在反序列化期间在 setter 中接收 XmlCharacterData 值,这就是在 setter 中对 .Value 的调用(我最初将它作为 ToString() 从内存中获取,但是这是不正确的。)
  • @PhilipRieck 如果我们需要在 CDataSection 周围包装自定义对象怎么办。创建 CDataSection 接受字符串。
  • 谢谢!最简单的解决方案。对我来说很好。
【解决方案3】:
[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
}

【讨论】:

  • 对我来说,这似乎不是最优雅的解决方案。这是唯一可行的方法吗?
  • 我认为这是实现这一目标的唯一方法,我在其他地方看到过这个主题并且总是给出相同的答案。 Philip 的例子更简洁一些,但概念相同。我知道的唯一其他方法是在表示 CDATA 内容的类上实现您自己的 msdn.microsoft.com/en-us/library/…>。
  • 我想做同样的事情,因为它似乎将字符串存储为 CDATA 似乎意味着更少的处理时间,因为我们可以“仅”读/写字符串“原样”。涉及 XmlDocument/XmlCDataSection 实例的成本有多高?
  • 整个 Attributes 都在那里,因此我们能够保持域模型类与序列化逻辑细节无关。如果肮脏的方式是唯一的方式,那真是太可悲了。
  • Philip 的解决方案在页面稍靠下的位置是一件更整洁的事情。
【解决方案4】:

在要序列化的类中:

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

【讨论】:

  • 像魅力一样工作。谢谢。
  • 这个答案值得更多的认可。虽然,自定义的 CData 类型不再具有 System.String 类型所享有的那些方便的内置方法。
  • 很好,然后先回答
  • 答案效果很好。遗憾的是 XmlElement 不适用于字符串字段,那么您可以添加一个 cdata 类型,但无论如何......
  • 完美!谢谢!
【解决方案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>

【讨论】:

  • 我需要这样做。谢谢!!
【解决方案6】:

在我的情况下,我使用的是混合字段,一些 CDATA 一些不是, 至少对我来说,以下解决方案正在工作....

通过始终读取值字段,我可以获取内容,无论是 CDATA 还是纯文本。

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

    [XmlText]
    public string Value;

迟到总比没有好。

干杯

【讨论】:

  • 太棒了 - 我觉得这个答案为我节省了很多时间!有关信息,我在 Value 上使用了 [XmlIgnore] 属性
  • 这在操作上与pr0gg3r's answer 有何不同?
【解决方案7】:

此实现能够处理您正在编码的字符串中的嵌套 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!!]]>

以下实现将遍历字符串,将...]]&gt;... 的实例拆分为...]]&gt;...,并为每个实例创建单独的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;
            }
        }
    }

【讨论】:

    【解决方案8】:

    这很好用

    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Xml;
    using System.Xml.Serialization;
    
    public class CDataContent
    {
        public CDataContent()
        {
        }
    
        public CDataContent(string content)
        {
            this.Content = content;
        }
    
        [XmlIgnore]
        public string Content
        {
            get => this.CData.FirstOrDefault()?.Value;
            set
            {
                this.CData.Clear();
                this.CData.Add(new XmlDocument().CreateCDataSection(value));
            }
        }
    
        [XmlText]
        public Collection<XmlNode> CData { get; } = new();
    
        public static implicit operator CDataContent(string value) => new(value);
    
        public static implicit operator string(CDataContent value) => value.Content;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-13
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多