C# Xml serialization, collection and root element

前端 未结 3 592
攒了一身酷
攒了一身酷 2020-12-17 18:16

My app serializes objects in streams. Here is a sample of what I need :


  
  

        
3条回答
  •  抹茶落季
    2020-12-17 18:57

    Ok, here is my final solution (hope it helps someone), that can serialize a plain array, List<>, HashSet<>, ...

    To achieve this, we'll need to tell the serializer what root node to use, and it's kind of tricky...

    1) Use 'XmlType' on the serializable object

    [XmlType("link")]
    public class LinkFinalVersion
    {
        [XmlAttribute("href")]
        public string Url { get; set; }
    
        [XmlAttribute("rel")]
        public string Relationship { get; set; }
    }
    

    2) Code a 'smart-root-detector-for-collection' method, that will return a XmlRootAttribute

    private XmlRootAttribute XmlRootForCollection(Type type)
    {
        XmlRootAttribute result = null;
    
        Type typeInner = null;
        if(type.IsGenericType)
        {
            var typeGeneric = type.GetGenericArguments()[0];
            var typeCollection = typeof (ICollection<>).MakeGenericType(typeGeneric);
            if(typeCollection.IsAssignableFrom(type))
                typeInner = typeGeneric;
        }
        else if(typeof (ICollection).IsAssignableFrom(type)
            && type.HasElementType)
        {
            typeInner = type.GetElementType();
        }
    
        // yeepeeh ! if we are working with a collection
        if(typeInner != null)
        {
            var attributes = typeInner.GetCustomAttributes(typeof (XmlTypeAttribute), true);
            if((attributes != null)
                && (attributes.Length > 0))
            {
                var typeName = (attributes[0] as XmlTypeAttribute).TypeName + 's';
                result = new XmlRootAttribute(typeName);
            }
        }
        return result;
    }
    

    3) Push that XmlRootAttribute into the serializer

    // hack : get the XmlRootAttribute if the item is a collection
    var root = XmlRootForCollection(type);
    // create the serializer
    var serializer = new XmlSerializer(type, root);
    

    I told you it was tricky ;)


    To improve this, you can :

    A) Create a XmlTypeInCollectionAttribute to specify a custom root name (If the basic pluralization does not fit your need)

    [XmlType("link")]
    [XmlTypeInCollection("links")]
    public class LinkFinalVersion
    {
    }
    

    B) If possible, cache your XmlSerializer (in a static Dictionary for example).

    In my testing, instanciating a XmlSerializer without the XmlRootAttributes takes 3ms. If you specify an XmlRootAttribute, it takes around 80ms (Just to have a custom root node name !)

提交回复
热议问题