使用CData部分包装属性 - XML序列化C#

时间:2015-12-07 07:53:15

标签: c# xml serialization xmlserializer cdata

我需要以这样的方式序列化我的对象,即我想要的属性将被包裹在CData部分。 我希望我能做到这样的事情:

public class Order
    {
        [JsonProperty]
        public int OrderId { get; set; }
        [JsonProperty]
        public string Name { get; set; }
        [JsonProperty]
        public int Type { get; set; }
        [JsonProperty]
        public decimal Amount { get; set; }
        [JsonProperty]
        public DateTime Date { get; set; }
        [DataMember]
        [JsonProperty]
        **[WrapCData]**
        public List<Option> ListB { get; set; }
        [DataMember]
        public List<string> ListC { get; set; }
        **[WrapCData]**
        public Product Product { get; set; }
    }

是否有任何属性或实现可以将我的特定属性包装在CData部分?现有的StackOverflow答案建议摆弄实体(类)本身。这会变得非常混乱。

在以下主题中: How do you serialize a string as CDATA using XmlSerializer?

Philip的回答建议制作另一个属性及其相应的CData属性。但是该属性是一个字符串。 CreateCDataSection()也接受一个字符串。我需要在CDataSections周围包装我的自定义对象/列表。我怎样才能做到这一点?任何帮助,将不胜感激。感谢。

以上订单类的示例XML:

<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <OrderId>2</OrderId>
  <Name>Some Name</Name>
  <Type>1</Type>
  <Amount>100</Amount>
  <Date>2015-12-07T15:10:49.6031106+05:00</Date>
  <![CDATA[
<ListB>
    <Option>
      <OptionValue>OptionValue1</OptionValue>
      <OptionName>Option1</OptionName>
    </Option>
    <Option>
      <OptionValue>OptionValue2</OptionValue>
      <OptionName>Option2</OptionName>
    </Option>
  </ListB>
]]>
  <ListC>
    <string>ListItem1</string>
    <string>ListItem2</string>
  </ListC>
  <![CDATA[
  <Product>
    <ProductId>1</ProductId>
    <Name>ProductName</Name>
    <Type>Product Type</Type>
  </Product>
]]>
</Order>

1 个答案:

答案 0 :(得分:2)

通过一些努力和自定义,可以接近您想要的内容,但XmlSerializer始终将CData个节点放在容器元素的末尾。您的示例XML显示容器元素的特定节点之间的CData个节点。只要您不需要这种精确控制,就可以使用How do you serialize a string as CDATA using XmlSerializer?进行嵌套序列化,如下所示:

public class Order
{
    [JsonProperty]
    public int OrderId { get; set; }
    [JsonProperty]
    public string Name { get; set; }
    [JsonProperty]
    public int Type { get; set; }
    [JsonProperty]
    public decimal Amount { get; set; }
    [JsonProperty]
    public DateTime Date { get; set; }

    [DataMember]
    [JsonProperty]
    [XmlIgnore] // Do not serialize directly
    [XmlWrapCData] // Instead include in CDATA nodes
    public List<Option> ListB { get; set; }

    [DataMember]
    public List<string> ListC { get; set; }

    [XmlIgnore] // Do not serialize directly
    [XmlWrapCData] // Instead include in CDATA nodes
    public Product Product { get; set; }

    [XmlText] // NECESSARY TO EMIT CDATA NODES
    [IgnoreDataMember]
    [JsonIgnore]
    public XmlNode[] CDataContent
    {
        get
        {
            return XmlWrapCDataHelper.GetCDataContent(this);
        }
        set
        {
            XmlWrapCDataHelper.SetCDataContent(this, value);
        }
    }
}

public class Product
{
    public string ProductId { get; set; }
    public string Name { get; set; }
    public string Type { get; set; }
}

public class Option
{
    public string OptionValue { get; set; }
    public string OptionName { get; set; }
}

使用以下扩展方法和自定义属性:

[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class XmlWrapCDataAttribute : Attribute
{
    public XmlWrapCDataAttribute() { this.Namespace = string.Empty;  }
    public XmlWrapCDataAttribute(string name) : this() { this.Name = name; }

    public string Name { get; set; }

    public string Namespace { get; set; }
}

public static class XmlWrapCDataHelper
{
    static Tuple<PropertyInfo, XmlWrapCDataAttribute> [] XmlWrapCDataProperties(Type type)
    {
        return type.GetProperties()
            .Where(p => p.GetGetMethod() != null && p.GetSetMethod() != null)
            .Select(p => Tuple.Create(p, p.GetCustomAttribute<XmlWrapCDataAttribute>()))
            .Where(p => p.Item2 != null)
            .ToArray();
    }

    public static XmlNode[] GetCDataContent(object obj)
    {
        var index = new object[0];
        var properties = XmlWrapCDataProperties(obj.GetType());
        return properties.Select(p => (XmlNode)p.Item1.GetValue(obj, index).GetCData(p.Item2.Name ?? p.Item1.Name, p.Item2.Namespace)).ToArray();
    }

    public static void SetCDataContent(object obj, XmlNode [] nodes)
    {
        if (nodes == null || nodes.Length < 1)
            return;
        var index = new object[0];
        var properties = XmlWrapCDataProperties(obj.GetType()).ToDictionary(p => XName.Get(p.Item2.Name ?? p.Item1.Name, p.Item2.Namespace), p => p);
        var xml = "<Root>" + String.Concat(nodes.Select(c => c.Value)) + "</Root>";
        foreach (var element in XElement.Parse(xml).Elements())
        {
            Tuple<PropertyInfo, XmlWrapCDataAttribute> pair;
            if (properties.TryGetValue(element.Name, out pair))
            {
                var value = element.Deserialize(pair.Item1.PropertyType, element.Name.LocalName, element.Name.Namespace.NamespaceName);
                pair.Item1.SetValue(obj, value, index);
            }
        }
    }
}

public static class XmlSerializationHelper
{
    public static XmlCDataSection GetCData(this object obj, string rootName, string rootNamespace)
    {
        return obj == null ? null : new System.Xml.XmlDocument().CreateCDataSection(obj.GetXml(XmlSerializerFactory.Create(obj.GetType(), rootName, rootNamespace)));
    }

    public static XCData GetCData(this object obj, XmlSerializer serializer = null)
    {
        return obj == null ? null : new XCData(obj.GetXml(serializer));
    }

    public static string GetXml(this object obj, XmlSerializer serializer = null)
    {
        using (var textWriter = new StringWriter())
        {
            var ns = new XmlSerializerNamespaces();
            ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
            var settings = new XmlWriterSettings() { Indent = true, IndentChars = "  ", OmitXmlDeclaration = true }; // For cosmetic purposes.
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                (serializer ?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj, ns);
            return textWriter.ToString();
        }
    }

    public static object Deserialize(this XContainer element, Type type, string rootName = null, string rootNamespace = null)
    {
        return element.Deserialize(type, XmlSerializerFactory.Create(type, rootName, rootNamespace));
    }

    public static object Deserialize(this XContainer element, Type type, XmlSerializer serializer = null)
    {
        using (var reader = element.CreateReader())
        {
            return (serializer ?? new XmlSerializer(type)).Deserialize(reader);
        }
    }

    public static T DeserializeXML<T>(this string xmlString, XmlSerializer serializer = null)
    {
        using (StringReader reader = new StringReader(xmlString))
        {
            return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
        }
    }
}

public static class XmlSerializerFactory
{
    readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
    readonly static object padlock;

    static XmlSerializerFactory()
    {
        padlock = new object();
        cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
    }

    public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
    {
        if (serializedType == null)
            throw new ArgumentNullException();
        if (rootName == null && rootNamespace == null)
            return new XmlSerializer(serializedType);
        lock (padlock)
        {
            XmlSerializer serializer;
            var key = Tuple.Create(serializedType, rootName, rootNamespace);
            if (!cache.TryGetValue(key, out serializer))
                cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
            return serializer;
        }
    }
}

这将成功解析您提供的XML,并返回生成如下所示的XML:

<Order>
  <OrderId>2</OrderId>
  <Name>Some Name</Name>
  <Type>1</Type>
  <Amount>100</Amount>
  <Date>2015-12-07T05:10:49.6031106-05:00</Date>
  <ListC>
    <string>ListItem1</string>
    <string>ListItem2</string>
  </ListC><![CDATA[<ListB>
  <Option>
    <OptionValue>OptionValue1</OptionValue>
    <OptionName>Option1</OptionName>
  </Option>
  <Option>
    <OptionValue>OptionValue2</OptionValue>
    <OptionName>Option2</OptionName>
  </Option>
</ListB>]]><![CDATA[<Product>
  <ProductId>1</ProductId>
  <Name>ProductName</Name>
  <Type>Product Type</Type>
</Product>]]></Order>