c# - Deserialize XML with XmlSerializer where XmlElement names differ but have same content -


i deserialize xml file class several subclasses. xml looks this:

<?xml version="1.0" encoding="utf-8"?> <objects>     <group index="1">         <de>             <groupname>antrieb</groupname>         </de>         <en>             <groupname>missing translation!</groupname>         </en>         <level>2</level>     </group>     <group index="2">         <de>             <groupname>bremsen</groupname>         </de>         <level>3</level>     </group> </objects> 

deserializing xml classes no problem, if there wouldn't language tags. sure, create property every language tag possible. list of languages possible should dynamic (e.g. read config file).

this reason why deserialize language tags , content dictionary uses language key , model content.

my models this:

[xmlroot("objects")] public class deactivationsxml {     [xmlelement("group")]     public deactivationsgroup[] groups { get; set; } }  [serializable()] public class deactivationsgroup {     [xmlignore]     public dictionary<string, groupname> groupnames { get; set; } = new dictionary<string, groupname>();      public int level { get; set; }      [xmlattribute]     public byte index { get; set; } }  public class groupname {     [xmlelement("groupname")]     public string name { get; set; } } 

i searched long time address problem, couldn't find solution. i'm pretty sure, it's not possible solve problem attributes.

does hybrid aproach exist in order combine deserialization of xml file in combination manual deserialization of xmlelements not automatically deserialized?

a , extensible solution problem great, because xml structure complex (same problem several times different content etc.). can't change structure of xml, please don't point out.

approaches

ixmlserializable

i tried implement ixmlserializable interface on deactivationsgroup class in order search list of given languages xmlelements names , deserialize content of xmlelements.

but approach didn't work out, because have map properties manually.

iextensibledataobject

the interface supported datacontractserializer. in worst case use interface deserialize after deserializing, if no other solution found..

ondeserialization

this attribute not supported xmlserializer, provide functionality possibly need.

xmlanyelement

i guess best option @ point. callback exist after deserialization finished in order automate this?

executable code

here's whole code far.

public void parse() {     string xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +  "    <objects>" +  "       <group index=\"1\">" +  "           <de>" +  "               <groupname>antrieb</groupname>" +  "           </de>" +  "           <en>" +  "               <groupname>missing translation!</groupname>" +  "           </en>" +  "           <level>2</level>" +  "       </group>" +  "       <group index=\"2\">" +  "           <de>" +  "               <groupname>bremsen</groupname>" +  "           </de>" +  "           <level>3</level>" +  "       </group>" +  "    </objects>";      xmlserializer serializer = new xmlserializer(typeof(deactivationsxml));      using (textreader filestream = new stringreader(xml))     {         var result = (deactivationsxml)serializer.deserialize(filestream);     } }  [xmlroot("objects")] public class deactivationsxml {     [xmlelement("group")]     public deactivationsgroup[] groups { get; set; } }  [serializable()] public class deactivationsgroup {     [xmlignore]     public dictionary<string, groupname> groupnames { get; set; } = new dictionary<string, groupname>();      public int level { get; set; }      [xmlattribute]     public byte index { get; set; } }  public class groupname {     [xmlelement("groupname")]     public string name { get; set; } } 

you can adopt approach this answer , add surrogate xmlelement [] property, marked [xmlanyelement], performs nested (de)serialization on key/value pairs of dictionary<string, groupname> property, binding dictionary keys element names.

note that, while documentation xmlanyelementattribute states

specifies member (a field returns array of xmlelement or xmlnode objects) contains objects represent xml element has no corresponding member in object being serialized or deserialized.

in fact attribute can applied property well. (de)serialization callback not required since nested serialization can performed inside getter , setter surrogate property itself. can applied members returning array of xelement objects instead of xmlelement if prefer new linq-to-xml api.

in approach, deactivationsgroup like:

[serializable()] public class deactivationsgroup {     public deactivationsgroup() { this.groupnames = new dictionary<string, groupname>(); }      [xmlignore]     public dictionary<string, groupname> groupnames { get; set; }      public int level { get; set; }      [xmlattribute]     public byte index { get; set; }      [xmlanyelement]     [browsable(false), editorbrowsable(editorbrowsablestate.never), debuggerbrowsable(debuggerbrowsablestate.never)]     public xelement[] xmlgroupnames     {                 {             return groupnames.serializetoxelements(null);         }         set         {             if (value == null || value.length < 1)                 return;             foreach (var pair in value.deserializefromxelements<groupname>())             {                 groupnames.add(pair.key, pair.value);             }         }     } } 

making use of following extension methods , classes:

public static class xmlkeyvaluelisthelper {     const string rootlocalname = "root";      public static xelement [] serializetoxelements<t>(this idictionary<string, t> dictionary, xnamespace ns)     {         if (dictionary == null)             return null;         ns = ns ?? "";         var serializer = xmlserializerfactory.create(typeof(t), rootlocalname, ns.namespacename);         var array = dictionary             .select(p => new { p.key, value = p.value.serializetoxelement(serializer, true) })             // fix name , remove redundant xmlns= attributes.  xmlwriter add them if needed.             .select(p => new xelement(ns + p.key, p.value.attributes().where(a => !a.isnamespacedeclaration), p.value.elements()))             .toarray();         return array;     }      public static ienumerable<keyvaluepair<string, t>> deserializefromxelements<t>(this ienumerable<xelement> elements)     {         if (elements == null)             yield break;         xmlserializer serializer = null;         xnamespace ns = null;         foreach (var element in elements)         {             if (serializer == null || element.name.namespace != ns)             {                 ns = element.name.namespace;                 serializer = xmlserializerfactory.create(typeof(t), rootlocalname, ns.namespacename);             }             var elementtodeserialize = new xelement(ns + rootlocalname, element.attributes(), element.elements());             yield return new keyvaluepair<string, t>(element.name.localname, elementtodeserialize.deserialize<t>(serializer));         }     }      public static xmlserializernamespaces nostandardxmlnamespaces()     {         var ns = new xmlserializernamespaces();         ns.add("", ""); // disable xmlns:xsi , xmlns:xsd lines.         return ns;     }      public static xelement serializetoxelement<t>(this t obj)     {         return obj.serializetoxelement(null, nostandardxmlnamespaces());     }      public static xelement serializetoxelement<t>(this t obj, xmlserializernamespaces ns)     {         return obj.serializetoxelement(null, ns);     }      public static xelement serializetoxelement<t>(this t obj, xmlserializer serializer, bool omitstandardnamespaces)     {         return obj.serializetoxelement(serializer, (omitstandardnamespaces ? nostandardxmlnamespaces() : null));     }      public static xelement serializetoxelement<t>(this t obj, xmlserializer serializer, xmlserializernamespaces ns)     {         var doc = new xdocument();         using (var writer = doc.createwriter())             (serializer ?? new xmlserializer(obj.gettype())).serialize(writer, obj, ns);         var element = doc.root;         if (element != null)             element.remove();         return element;     }      public static t deserialize<t>(this xcontainer element, xmlserializer serializer)     {         using (var reader = element.createreader())         {             object result = (serializer ?? new xmlserializer(typeof(t))).deserialize(reader);             return (t)result;         }     } }  public static class xmlserializerfactory {     // avoid memory leak serializer must cached.     // https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer     // factory taken      // https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648      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;         }     } } 

sample fiddle. , another demonstrating case xml namespaces , attributes.


Comments

Popular posts from this blog

What is happening when Matlab is starting a "parallel pool"? -

angular - DownloadURL return null in below code -

php - Cannot override Laravel Spark authentication with own implementation -