使用 CData 部分包装属性 - XML 序列化 C# [英] Wrap properties with CData Section - XML Serialization C#

查看:30
本文介绍了使用 CData 部分包装属性 - XML 序列化 C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要以这样一种方式序列化我的对象,我想要的属性会被包裹在 CData 部分中.我希望我能做这样的事情:

公共类订单{[Json属性]公共 int OrderId { 获取;放;}[Json属性]公共字符串名称 { 获取;放;}[Json属性]公共 int 类型 { 获取;放;}[Json属性]公共十进制金额{得到;放;}[Json属性]公共日期时间日期{获取;放;}[数据成员][Json属性]**[WrapCData]**公共列表<选项>ListB { 得到;放;}[数据成员]公共列表<字符串>ListC { 得到;放;}**[WrapCData]**公共产品产品{得到;放;}}

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

在以下线程中:如何使用 XmlSerializer 将字符串序列化为 CDATA?

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><名称>一些名称</名称><类型>1</类型><数量>100</数量><日期>2015-12-07T15:10:49.6031106+05:00</日期><![CDATA[<列表B><选项><OptionValue>OptionValue1</OptionValue><OptionName>Option1</OptionName></选项><选项><OptionValue>OptionValue2</OptionValue><OptionName>Option2</OptionName></选项></ListB>]]><列表C><string>ListItem1</string><string>ListItem2</string></ListC><![CDATA[<产品><ProductId>1</ProductId><名称>产品名称</名称><类型>产品类型</类型></产品>]]></订单>

解决方案

通过一些努力和定制,可以接近你想要的,但是 XmlSerializer 将始终放置 CData 节点在容器元素的末尾.您的示例 XML 显示了容器元素的特定节点之间的 CData 节点.只要你不需要这种精确的控制,你可以使用 如何使用 XmlSerializer 将字符串序列化为 CDATA? 进行嵌套序列化,如下所示:

公共类订单{[Json属性]公共 int OrderId { 获取;放;}[Json属性]公共字符串名称 { 获取;放;}[Json属性]公共 int 类型 { 获取;放;}[Json属性]公共十进制金额{得到;放;}[Json属性]公共日期时间日期{获取;放;}[数据成员][Json属性][XmlIgnore]//不直接序列化[XmlWrapCData]//而是包含在 CDATA 节点中公共列表<选项>ListB { 得到;放;}[数据成员]公共列表<字符串>ListC { 得到;放;}[XmlIgnore]//不直接序列化[XmlWrapCData]//而是包含在 CDATA 节点中公共产品产品{得到;放;}[XmlText]//需要发出 CDATA 节点[忽略数据成员][Json忽略]公共 XmlNode[] CDataContent{得到{返回 XmlWrapCDataHelper.GetCDataContent(this);}放{XmlWrapCDataHelper.SetCDataContent(this, value);}}}公开课产品{公共字符串 ProductId { 获取;放;}公共字符串名称 { 获取;放;}公共字符串类型{获取;放;}}公开课选项{公共字符串 OptionValue { 获取;放;}公共字符串 OptionName { 获取;放;}}

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

[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]公共类 XmlWrapCDataAttribute : 属性{public XmlWrapCDataAttribute() { this.Namespace = string.Empty;}public XmlWrapCDataAttribute(string name) : this() { this.Name = name;}公共字符串名称 { 获取;放;}公共字符串命名空间{获取;放;}}公共静态类 XmlWrapCDataHelper{静态元组[] XmlWrapCDataProperties(类型类型){返回类型.GetProperties().Where(p => p.GetGetMethod() != null && p.GetSetMethod() != null).Select(p => Tuple.Create(p, p.GetCustomAttribute())).Where(p => p.Item2 != null).ToArray();}公共静态 XmlNode[] GetCDataContent(object obj){var index = 新对象[0];var 属性 = XmlWrapCDataProperties(obj.GetType());返回 properties.Select(p => (XmlNode)p.Item1.GetValue(obj, index).GetCData(p.Item2.Name ?? p.Item1.Name, p.Item2.Namespace)).ToArray();}公共静态无效 SetCDataContent(对象 obj,XmlNode [] 节点){if (nodes == null || nodes.Length <1)返回;var index = 新对象[0];var properties = XmlWrapCDataProperties(obj.GetType()).ToDictionary(p => XName.Get(p.Item2.Name ?? p.Item1.Name, p.Item2.Namespace), p => p);var xml = ""+ String.Concat(nodes.Select(c => c.Value)) + "</Root>";foreach(XElement.Parse(xml).Elements() 中的 var 元素){元组一对;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);}}}}公共静态类 XmlSerializationHelper{public static XmlCDataSection GetCData(这个对象obj,字符串rootName,字符串rootNamespace){返回 obj == null ?null : new System.Xml.XmlDocument().CreateCDataSection(obj.GetXml(XmlSerializerFactory.Create(obj.GetType(), rootName, rootNamespace)));}公共静态 XCData GetCData(此对象 obj,XmlSerializer 序列化程序 = null){返回 obj == null ?null : new XCData(obj.GetXml(serializer));}公共静态字符串 GetXml(此对象 obj,XmlSerializer 序列化程序 = null){使用 (var textWriter = new StringWriter()){var ns = new XmlSerializerNamespaces();ns.Add("", "");//禁用 xmlns:xsi 和 xmlns:xsd 行.var settings = new XmlWriterSettings() { Indent = true, IndentChars = " ", OmitXmlDeclaration = true };//用于装饰目的.使用 (var xmlWriter = XmlWriter.Create(textWriter, settings))(序列化器?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj, ns);返回 textWriter.ToString();}}公共静态对象反序列化(此 XContainer 元素,类型类型,字符串 rootName = null,字符串 rootNamespace = null){return element.Deserialize(type, XmlSerializerFactory.Create(type, rootName, rootNamespace));}公共静态对象反序列化(此 XContainer 元素,类型类型,XmlSerializer 序列化器 = null){使用 (var reader = element.CreateReader()){return (serializer ?? new XmlSerializer(type)).Deserialize(reader);}}public static T DeserializeXML(这个字符串xmlString, XmlSerializer serializer = null){使用 (StringReader reader = new StringReader(xmlString)){return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);}}}公共静态类 XmlSerializerFactory{readonly static Dictionary, XmlSerializer>缓存;只读静态对象挂锁;静态 XmlSerializerFactory(){挂锁 = 新对象();cache = new Dictionary, XmlSerializer>();}public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace){if (serializedType == null)抛出新的 ArgumentNullException();if (rootName == null && rootNamespace == null)返回新的 XmlSerializer(serializedType);锁(挂锁){XmlSerializer 序列化器;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 });返回序列化程序;}}}

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

<块引用>

<OrderId>2</OrderId><名称>一些名称</名称><类型>1</类型><数量>100</数量><日期>2015-12-07T05:10:49.6031106-05:00</日期><列表C><string>ListItem1</string><string>ListItem2</string></ListC><![CDATA[<ListB><选项><OptionValue>OptionValue1</OptionValue><OptionName>Option1</OptionName></选项><选项><OptionValue>OptionValue2</OptionValue><OptionName>Option2</OptionName></选项></ListB>]]><![CDATA[<Product><ProductId>1</ProductId><名称>产品名称</名称><类型>产品类型</类型></产品>]]></订单>

I need to serialize my object in such a way that the properties I want, would get wrapped around CData sections. I was hoping I could do something like this :

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

Is there any attribute or an implementation which could wrap my specific properties around a CData section? Existing StackOverflow answers suggest fiddling with the Entity(Class) itself. This would get really messy.

In the following thread : How do you serialize a string as CDATA using XmlSerializer?

Philip's answer suggests to make another property and its corresponding CData property. However the property is a string. CreateCDataSection() also takes a string. I need to wrap my custom objects/lists around CDataSections. How can I do that? Any help would be appreciated. Thanks.

Sample XML for the above Order Class:

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

解决方案

With some effort and customization, it possible to get close to what you want, however XmlSerializer will always place the CData nodes at the end of the container element. Your example XML shows the CData nodes between specific nodes of the container element. As long as you don't need this precise control, you can use How do you serialize a string as CDATA using XmlSerializer? to do nested serializations, like so:

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

Using the following extension methods and custom attribute:

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

This will parse your provided XML successfully, and in return generate XML that looks like:

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

这篇关于使用 CData 部分包装属性 - XML 序列化 C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆