2008-10-18 8 views
43

J'ai une classe simple qui contient essentiellement quelques valeurs. J'ai surchargé la méthode ToString() pour renvoyer une belle représentation de chaîne.Création d'un XmlNode/XmlElement en C# sans XmlDocument?

Maintenant, je veux créer une méthode ToXml(), qui renverra quelque chose comme ceci:

<Song> 
    <Artist>Bla</Artist> 
    <Title>Foo</Title> 
</Song> 

Bien sûr, je pourrais simplement utiliser un StringBuilder ici, mais je voudrais revenir un XmlNode ou XmlElement, à utiliser avec XmlDocument.AppendChild.

Je ne semble pas être en mesure de créer un XmlElement autre que d'appeler XmlDocument.CreateElement, alors je me demande si je viens de rien oublié, ou si je vraiment avoir soit passer soit dans un XmlDocument ou ref XmlElement à travailler avec, ou ont la fonction retourne une chaîne qui contient le XML que je veux?

+0

Le titre de la question ne correspond pas à la question du contenu/objectif. Vous voulez savoir comment sérialiser vos classes. J'ai besoin d'une instance de XmlNode pour le passer en paramètre webservice. L'utilisateur va créer le XmlNode à partir de la chaîne d'entrée. –

+1

@DaviFiamenghi - Votre commentaire n'est pas correct. Si quelqu'un choisit de construire manuellement des données XML en utilisant XmlNode, ce qui est leur choix, il ne semble pas y avoir moyen de créer de tels objets XmlNode dans .Net sans le créer à partir d'un XmlDocument. – antiduh

Répondre

13

Vous pouvez Vous voulez voir comment vous pouvez utiliser les fonctions intégrées de .NET pour sérialiser et désérialiser un objet en XML, plutôt que de créer une méthode ToXML() sur chaque classe qui est essentiellement un objet de transfert de données.

J'ai utilisé ces techniques avec succès sur quelques projets mais je n'ai pas les détails d'implémentation à portée de main pour le moment. Je vais essayer de mettre à jour ma réponse avec mes propres exemples plus tard.

est ici quelques exemples que Google est revenu:

sérialisation XML dans .NET par Venkat Subramaniam http://www.agiledeveloper.com/articles/XMLSerialization.pdf

Comment sérialiser et désérialiser un objet en XML http://www.dotnetfunda.com/articles/article98.aspx

Personnalisez votre XML objet .NET sérialisation avec les attributs XML .NET http://blogs.microsoft.co.il/blogs/rotemb/archive/2008/07/27/customize-your-net-object-xml-serialization-with-net-xml-attributes.aspx

2

Vous avez besoin de Linq - System.Xml.Linq pour être précis.

Vous pouvez créer du XML en utilisant XElement à partir de zéro - cela devrait vous aider à vous débrouiller.

15

du W3C spécification Document Object Model (Core) Level 1 (gras est le mien):

La plupart des API définies par cette spécification sont des interfaces plutôt que classes. Cela signifie qu'une implémentation réelle doit seulement exposer méthodes avec les noms définis et opération spécifiée, pas réellement classes d'outil qui correspondent directement aux interfaces. Cette permet aux API DOM à mettre en œuvre comme un placage mince sur le dessus de l'héritage applications avec leurs propres données structures, ou sur des nouvelles applications avec différentes hiérarchies de classes . Cela signifie également que les constructeurs ordinaires (en Java ou C++ sens) ne peuvent pas être utilisés pour créer objets DOM, puisque les objets sous-jacents à construire peut avoir peu de rapport avec les interfaces DOM . La solution classique à cette conception orientée objet est pour définir des méthodes d'usine qui créent instances d'objets qui implémentent les différentes interfaces. Dans le DOM niveau 1, les objets implémentant certaines interface "X" sont créés par une méthode "createX()" sur l'interface Document ; c'est parce que tous les objets DOM vivent dans le contexte d'un document spécifique .

AFAIK, vous ne pouvez pas créer XmlNode (XmlElement, XmlAttribute, XmlCDataSection, etc.) à l'exception XmlDocument d'un constructeur.

De plus, notez que vous ne pouvez pas utiliser XmlDocument.AppendChild() pour les noeuds qui ne sont pas créés par les méthodes d'usine du même document . Dans le cas où vous avez un nœud d'un autre document, vous devez utiliser XmlDocument.ImportNode().

40

Je recommanderais d'utiliser XDoc et XElement de System.Xml.Linq au lieu de XmlDocument. Ce serait mieux et vous serez en mesure d'utiliser la puissance LINQ dans l'interrogation et l'analyse de votre XML: méthode

En utilisant XElement, votre ToXml() ressemblera à ce qui suit:

public XElement ToXml() 
{ 
    XElement element = new XElement("Song", 
         new XElement("Artist", "bla"), 
         new XElement("Title", "Foo")); 

    return element; 
} 
1

Vous ne pouvez pas renvoyer un XmlElement ou un XmlNode, parce que ces objets existent toujours et seulement dans le contexte d'un propriétaire XmlDocument.

La sérialisation XML est un peu plus facile que de retourner un XElement, car il suffit de marquer les propriétés avec des attributs et le sérialiseur effectue toute la génération XML pour vous. (De plus, vous obtenez la désérialisation gratuitement, en supposant que vous avez un constructeur sans paramètre et, bien, un tas d'autres choses.)

D'autre part, a) vous devez créer un XmlSerializer pour le faire, b) les propriétés de la collection ne sont pas tout à fait le non-dire que vous pourriez aimer, et c) la sérialisation XML est assez bête; vous n'avez pas de chance si vous voulez faire quelque chose de sophistiqué avec le XML que vous générez.

Dans beaucoup de cas, ces problèmes n'ont pas d'importance. Pour ma part, je préfère marquer mes propriétés avec des attributs que d'écrire une méthode.

4

Vous pouvez retourner un XmlDocument pour la méthode ToXML dans votre classe, lorsque vous allez ajouter l'élément avec le document de résultat juste utiliser quelque chose comme:

XmlDocument returnedDocument = Your_Class.ToXML(); 

XmlDocument finalDocument = new XmlDocument(); 
XmlElement createdElement = finalDocument.CreateElement("Desired_Element_Name"); 
createdElement.InnerXML = docResult.InnerXML; 
finalDocument.AppendChild(createdElement); 

De cette façon, la valeur entière pour « Desired_Element_Name "sur votre résultat XmlDocument sera l'intégralité du contenu du document retourné.

J'espère que cette aide.

4

Créer une nouvelle XmlDocument avec le contenu que vous voulez, puis l'importer dans votre document existant, en accédant à la propriété OwnerDocument de vos noeuds existants:

XmlNode existing_node; // of some document, where we don't know necessarily know the XmlDocument... 
XmlDocument temp = new XmlDocument(); 
temp.LoadXml("<new><elements/></new>"); 
XmlNode new_node = existing_node.OwnerDocument.ImportNode(temp.DocumentElement, true); 
existing_node.AppendChild(new_node); 

Bonne chance.

2

Une autre option consiste à passer un délégué à la méthode, ce qui créera un XmlElement. De cette façon, la méthode cible n'aura pas accès à XmlDocument entier, mais sera capable de créer de nouveaux éléments.

-2
XmlDocumnt xdoc = new XmlDocument; 
XmlNode songNode = xdoc.CreateNode(XmlNodeType.Element, "Song", schema) 
xdoc.AppendChild..... 
8

Les XmlNodes possèdent une propriété OwnerDocument.

Peut-être que vous pouvez juste faire:

//Node is an XmlNode pulled from an XmlDocument 
XmlElement e = node.OwnerDocument.CreateElement("MyNewElement"); 
e.InnerText = "Some value"; 
node.AppendChild(e); 
2

Pourquoi ne pas envisager la création de votre classe de données (es) comme un simple XmlDocument sous-classé, vous obtenez tout cela gratuitement. Vous n'avez pas besoin de sérialiser ou de créer des nœuds hors-doc, et vous obtenez la structure que vous voulez.

Si vous voulez le rendre plus sophistiqué, écrivez une classe de base qui est une sous-classe de XmlDocument, puis donnez-lui des accesseurs de base, et vous êtes défini.

est ici un type générique je mets ensemble pour un projet ...

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Xml; 
using System.IO; 

namespace FWFWLib { 
    public abstract class ContainerDoc : XmlDocument { 

     protected XmlElement root = null; 
     protected const string XPATH_BASE = "/$DATA_TYPE$"; 
     protected const string XPATH_SINGLE_FIELD = "/$DATA_TYPE$/$FIELD_NAME$"; 

     protected const string DOC_DATE_FORMAT = "yyyyMMdd"; 
     protected const string DOC_TIME_FORMAT = "HHmmssfff"; 
     protected const string DOC_DATE_TIME_FORMAT = DOC_DATE_FORMAT + DOC_TIME_FORMAT; 

     protected readonly string datatypeName = "containerDoc"; 
     protected readonly string execid = System.Guid.NewGuid().ToString().Replace("-", ""); 

     #region startup and teardown 
     public ContainerDoc(string execid, string datatypeName) { 
      root = this.DocumentElement; 
      this.datatypeName = datatypeName; 
      this.execid = execid; 
      if(null == datatypeName || "" == datatypeName.Trim()) { 
       throw new InvalidDataException("Data type name can not be blank"); 
      } 
      Init(); 
     } 

     public ContainerDoc(string datatypeName) { 
      root = this.DocumentElement; 
      this.datatypeName = datatypeName; 
      if(null == datatypeName || "" == datatypeName.Trim()) { 
       throw new InvalidDataException("Data type name can not be blank"); 
      } 
      Init(); 
     } 

     private ContainerDoc() { /*...*/ } 

     protected virtual void Init() { 
      string basexpath = XPATH_BASE.Replace("$DATA_TYPE$", datatypeName); 
      root = (XmlElement)this.SelectSingleNode(basexpath); 
      if(null == root) { 
       root = this.CreateElement(datatypeName); 
       this.AppendChild(root); 
      } 
      SetFieldValue("createdate", DateTime.Now.ToString(DOC_DATE_FORMAT)); 
      SetFieldValue("createtime", DateTime.Now.ToString(DOC_TIME_FORMAT)); 
     } 
     #endregion 

     #region setting/getting data fields 
     public virtual void SetFieldValue(string fieldname, object val) { 
      if(null == fieldname || "" == fieldname.Trim()) { 
       return; 
      } 
      fieldname = fieldname.Replace(" ", "_").ToLower(); 
      string xpath = XPATH_SINGLE_FIELD.Replace("$FIELD_NAME$", fieldname).Replace("$DATA_TYPE$", datatypeName); 
      XmlNode node = this.SelectSingleNode(xpath); 
      if(null != node) { 
       if(null != val) { 
        node.InnerText = val.ToString(); 
       } 
      } else { 
       node = this.CreateElement(fieldname); 
       if(null != val) { 
        node.InnerText = val.ToString(); 
       } 
       root.AppendChild(node); 
      } 
     } 

     public virtual string FieldValue(string fieldname) { 
      if(null == fieldname) { 
       fieldname = ""; 
      } 
      fieldname = fieldname.ToLower().Trim(); 
      string rtn = ""; 
      XmlNode node = this.SelectSingleNode(XPATH_SINGLE_FIELD.Replace("$FIELD_NAME$", fieldname).Replace("$DATA_TYPE$", datatypeName)); 
      if(null != node) { 
       rtn = node.InnerText; 
      } 
      return rtn.Trim(); 
     } 

     public virtual string ToXml() { 
      return this.OuterXml; 
     } 

     public override string ToString() { 
      return ToXml(); 
     } 
     #endregion 

     #region io 
     public void WriteTo(string filename) { 
      TextWriter tw = new StreamWriter(filename); 
      tw.WriteLine(this.OuterXml); 
      tw.Close(); 
      tw.Dispose(); 
     } 

     public void WriteTo(Stream strm) { 
      TextWriter tw = new StreamWriter(strm); 
      tw.WriteLine(this.OuterXml); 
      tw.Close(); 
      tw.Dispose(); 
     } 

     public void WriteTo(TextWriter writer) { 
      writer.WriteLine(this.OuterXml); 
     } 
     #endregion 

    } 
} 
Questions connexes