简体   繁体   English

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

[英]Wrap properties with CData Section - XML Serialization C#

I need to serialize my object in such a way that the properties I want, would get wrapped around CData sections. 我需要以一种我想要的属性被包裹在CData节中的方式来序列化我的对象。 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? 是否有任何属性或实现可以将我的特定属性包装在CData节周围? Existing StackOverflow answers suggest fiddling with the Entity(Class) itself. 现有的StackOverflow答案建议您摆弄Entity(Class)本身。 This would get really messy. 这会变得非常混乱。

In the following thread : How do you serialize a string as CDATA using XmlSerializer? 在以下线程中: 如何使用XmlSerializer将字符串序列化为CDATA?

Philip's answer suggests to make another property and its corresponding CData property. Philip的答案建议制作另一个属性及其对应的CData属性。 However the property is a string. 但是,该属性是字符串。 CreateCDataSection() also takes a string. CreateCDataSection()也接受一个字符串。 I need to wrap my custom objects/lists around CDataSections. 我需要在CDataSections周围包装我的自定义对象/列表。 How can I do that? 我怎样才能做到这一点? Any help would be appreciated. 任何帮助,将不胜感激。 Thanks. 谢谢。

Sample XML for the above Order Class: 上述订单类的示例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>

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 . 通过一些努力和自定义,可能会接近所需的内容,但是XmlSerializer始终将CData节点放置在container元素的末尾 Your example XML shows the CData nodes between specific nodes of the container element . 您的示例XML显示了容器元素的特定节点之间CData节点。 As long as you don't need this precise control, you can use How do you serialize a string as CDATA using XmlSerializer? 只要不需要此精确控件,就可以使用如何使用XmlSerializer将字符串序列化为CDATA? 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: 这将成功解析您提供的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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM