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

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

问题描述

我需要以一种我想要的属性被包裹在CData节中的方式来序列化我的对象。
我希望我可以做这样的事情:

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

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

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.

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

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

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.

上述订单类的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>


推荐答案

通过一些努力和自定义,有可能取得接近到您想要的内容,但是 XmlSerializer 始终将 CData 节点放在容器元素的末尾。您的示例XML显示了容器元素的特定节点之间的 CData 节点。只要您不需要此精确控制,就可以使用如何使用XmlSerializer将字符串序列化为CDATA?进行嵌套序列化,例如:

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

这将解析您提供的XML

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天全站免登陆