2010-07-16 7 views
1

J'ai rencontré un problème et je me suis demandé s'il y avait une façon simple de le résoudre.Sérialiser une liste <string> et utiliser chaque chaîne comme xml Noeud

Ici, j'ai un modèle XML, définissant certaines propriétés et leurs valeurs.

<Properties> 
    <Property name="ID">10000</Property> 
    <Property name="Name"> 
    <SubProperty name="FirstName">Foo</SubProperty> 
    <SubProperty name="LastName">Bar</SubProperty > 
    </Property> 
</Properties> 

Tout ce que je ce qui est d'extraire les propriétés/sous-propriétés définies dans le modèle pour générer un nouveau fichier XML, avec toutes les valeurs attachées, quelque chose comme

<Items> 
    <ID>10000</ID> 
    <Name> 
    <FirstName>Foo</FirstName> 
    <LastName>Bar</LastName> 
    </Name> 
</Items> 

Depuis que je ne connais pas le contenu du modèle lors de la conception, j'ai essayé de le charger et de créer une classe List en utilisant LINQ, mais je ne peux pas obtenir le résultat ci-dessus quand je le sérialise directement. Ainsi, au lieu de créer une classe List, j'ai créé un objet dynamique en utilisant Reflection.Emit, puis sérialisé l'objet en XML.


private static readonly XDocument doc = XDocument.Load("Template.xml"); 

static void Main(string[] args) { 
    var newType = CreateDynamicType(); 
    var newObject = Activator.CreateInstance(newType); 
    var properties = newType.GetProperties(); 
    foreach (var property in properties) { 
     // assign values 
    } 
    SerializeToXml(newObject); 
} 

private static Type CreateDynamicType() { 

    AssemblyName assemblyName = new AssemblyName() { Name = "DynamicTypeAdapter" }; 
    AssemblyBuilder assembly = 
     Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
    ModuleBuilder module = 
     assembly.DefineDynamicModule(assembly.GetName().Name, false); 
    TypeBuilder type = module.DefineType("Items", TypeAttributes.Public | TypeAttributes.Class); 

    foreach (var p in doc.Descendants("Property")) { 
     string pName = p.Attribute("name").Value; 
     TypeBuilder subType = module.DefineType(pName, TypeAttributes.Public | TypeAttributes.Class); 
     foreach (var sp in p.Descendants("SubProperty")) { 
      CreateDynamicProperty(subType, sp.Attribute("name").Value, typeof(string)); 
     } 
     var propertyType = subType.CreateType(); 
     CreateDynamicProperty(type, pName, propertyType); 
    } 

    return type.CreateType(); 
} 

private static void CreateDynamicProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType) { 
    PropertyBuilder property = typeBuilder.DefineProperty(propertyName, 
    PropertyAttributes.None, propertyType, new Type[] { typeof(string) }); 

    FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); 

    MethodAttributes GetSetAttributes = MethodAttributes.Public | MethodAttributes.HideBySig; 

    MethodBuilder getMethod = 
     typeBuilder.DefineMethod("get_value", GetSetAttributes, propertyType, Type.EmptyTypes); 

    ILGenerator getIL = getMethod.GetILGenerator(); 
    getIL.Emit(OpCodes.Ldarg_0); 
    getIL.Emit(OpCodes.Ldfld, field); 
    getIL.Emit(OpCodes.Ret); 

    MethodBuilder setMethod = 
      typeBuilder.DefineMethod("set_value", GetSetAttributes, null, new Type[] { typeof(string) }); 

    ILGenerator setIL = setMethod.GetILGenerator(); 
    setIL.Emit(OpCodes.Ldarg_0); 
    setIL.Emit(OpCodes.Ldarg_1); 
    setIL.Emit(OpCodes.Stfld, field); 
    setIL.Emit(OpCodes.Ret); 

    property.SetGetMethod(getMethod); 
    property.SetSetMethod(setMethod); 
} 

Il fonctionne très bien, mais est-il un moyen simple de le faire? Un commentaire est apprécié. Merci

+0

Vous avez présenté un problème, et certains échantillons XM L, mais j'ai encore du mal à comprendre ce que fait votre code. Pourriez-vous poster un exemple du code que vous avez actuellement? – jrista

+0

Merci pour votre commentaire. J'ai modifié le contenu, désolé pour le code désordonné. – Gnavvy

Répondre

2

Si tout ce que vous voulez faire changer (transformer) un format XML en un autre format XML alors je pense que l'approche que vous prenez n'est pas la plus appropriée. Il existe d'autres API dans le framework qui prennent en charge ce type de fonctionnalités. Dans votre cas, l'exigence semble plutôt simple, donc j'opterais pour l'option Linq To Xml. Voici un exemple rapide qui produit la sortie souhaitée.

XDocument doc = XDocument.Parse(@"<Properties> 
       <Property name='ID'>10000</Property> 
       <Property name='Name'> 
        <SubProperty name='FirstName'>Foo</SubProperty> 
        <SubProperty name='LastName'>Bar</SubProperty> 
       </Property> 
      </Properties>"); 

XElement items = new XElement("Items", 
           from property in doc.Descendants("Property") 
           select new XElement((string)property.Attribute("name"), 
                // If there are no child elements (SubPropety) 
                // get the property value 
                property.HasElements ? null : (string)property, 
                // Another way for checking if there are any child elements 
                // You could also use property.HasElements like the previous statement 
                property.Elements("SubProperty").Any() ? 
                from subproperty in property.Elements("SubProperty") 
                select new XElement((string)subproperty.Attribute("name"), 
                     (string)subproperty) : null) 

         ); 

Quelques resouces qui peuvent être utiles comprennent:

http://msdn.microsoft.com/en-us/library/bb387098.aspx

http://msdn.microsoft.com/en-us/library/bb308960.aspx

http://iqueryable.com/2007/08/03/TransformingXMLWithLINQToXML.aspx

http://www.codeproject.com/KB/linq/LINQtoXML.aspx

+0

Merci beaucoup pour la solution. – Gnavvy

1

Basé sur ce que vous avez ci-dessus, quelque chose de simple comme ceci devrait fonctionner.

var root = XElement.Parse(xml); 
var result = new XElement("Items"); 
foreach (var p in root.Descendants("Property")) 
{ 
var subs = p.Descendants("SubProperty").Select(sp => Transpose(sp)); 

    // The trick is here - XElement constructor uses params object[], 
    // so we can pass an arbitrary number of arguments to build the XElement 
var item = new XElement(p.Attribute("name").Value, subs, subs.Any() : null ? p.Value); 

    result.Add(item); 
} 

// Transpose method 
XElement Transpose(XElement xe) 
{ 
return new XElement(xe.Attribute("name").Value, xe.Value); 
} 


// result 
<Items> 
    <ID>10000</ID> 
    <Name> 
    <FirstName>Foo</FirstName> 
    <LastName>Bar</LastName> 
    </Name> 
</Items> 

REMARQUE: Si vous avez plusieurs niveaux d'imbrication, ou doivent être en mesure de différencier plusieurs Item noeuds dans le modèle, vous aurez besoin de penser un peu plus.

+0

Merci Winston, une aide précieuse – Gnavvy