2008-10-18 22 views
43

que tienen una clase simple que esencialmente sólo tiene algunos valores. He reemplazado el método ToString() para devolver una buena representación de cadena.Creando un XmlNode/XmlElement en C# sin un XmlDocument?

Ahora, yo quiero crear un método ToXml(), que devolverá algo como esto:

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

Por supuesto, sólo podría utilizar un StringBuilder aquí, pero me gustaría devolver un XmlNode o XmlElement, para ser utilizado con XmlDocument.AppendChild.

no parecen ser capaces de crear un XmlElement que no sea llamando XmlDocument.CreateElement, por lo que me pregunto si me he pasado por alto nada, o si realmente bien tienen que pasar ya sea en un XmlDocument o ref XmlElement para trabajar, o tienen la función devuelve una cadena que contiene el XML que quiero?

+0

título La pregunta no corresponde a la pregunta de contenido/objetivo. Desea saber cómo serializar sus clases. Necesito una instancia de XmlNode para pasarlo como parámetro de servicio web. El usuario creará XmlNode desde la cadena de entrada. –

+1

@DaviFiamenghi - Su comentario no es correcto. Si alguien elige crear manualmente datos XML utilizando XmlNode, que es su elección, no parece haber una manera de crear tales objetos XmlNode en .Net sin crearlo desde un XmlDocument. – antiduh

Respuesta

13

Usted puede desea ver cómo puede usar las funciones incorporadas de .NET para serializar y deserializar un objeto en XML, en lugar de crear un método ToXML() en cada clase que es esencialmente solo un Objeto de Transferencia de Datos.

He utilizado estas técnicas con éxito en un par de proyectos, pero no tengo los detalles de implementación a la mano en este momento. Trataré de actualizar mi respuesta con mis propios ejemplos algún tiempo después.

He aquí un par de ejemplos que Google volvió: serialización

XML en .NET por Venkat Subramaniam http://www.agiledeveloper.com/articles/XMLSerialization.pdf

Cómo serializar y deserializar un objeto en XML http://www.dotnetfunda.com/articles/article98.aspx

Personaliza tu .NET objeto XML serialización con atributos .NET XML http://blogs.microsoft.co.il/blogs/rotemb/archive/2008/07/27/customize-your-net-object-xml-serialization-with-net-xml-attributes.aspx

2

Usted necesita LINQ - System.Xml.Linq para ser precisos.

Puede crear XML utilizando XElement desde cero - que en caso de más o menos tipo que fuera.

15

De W3C especificación Document Object Model (Core) Level 1 (negrita es mía):

La mayoría de los APIs definidos por esta especificación son interfaces en lugar de clases. Eso significa que una implementación real de solo necesita exponer los métodos con los nombres definidos y la operación especificada , en realidad no implementar clases que corresponden a directamente a las interfaces. Esto permite las API DOM para implementarse como una fina capa en la parte superior del legado aplicaciones con sus propios datos estructuras, o en la parte superior de los nuevos aplicaciones con diferentes clases jerarquías. Esto también significa que constructores ordinarios (en el Java o C sentido ++) no se pueden utilizar para crear objetos DOM, puesto que los objetos subyacentes a ser construido puede tener poca relación con las interfaces DOM . La solución convencional para esto en diseño orientado a objetos es para definir los métodos de fábrica que crean instancias de objetos que implementan las diversas interfaces. En el DOM Nivel 1, los objetos de aplicación algún método interfaz "X" son creados por un "createX()" en el Documento interfaz; esto se debe a todos los objetos DOM viven en el contexto de un documento específico .

yo sepa, no se puede crear cualquier XmlNode (XmlElement, XmlAttribute, XmlCDataSection, etc) excepto XmlDocument de un constructor.

Además, tenga en cuenta que no puede usar XmlDocument.AppendChild() para nodos que no se crean mediante los métodos de fábrica del documento mismo. En caso de que tenga un nodo de otro documento, debe usar XmlDocument.ImportNode().

40

Recomendaría utilizar XDoc y XElement de System.Xml.Linq en lugar de cosas de XmlDocument. Esto sería mejor y que será capaz de hacer uso del poder de LINQ en consultar y analizar el código XML:

Usando XElement, su un ToXml() método se verá como la siguiente:

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

    return element; 
} 
1

No puede devolver un XmlElement o un XmlNode, porque esos objetos siempre y solo existen dentro del contexto de un propietario XmlDocument.

La serialización XML es un poco más fácil que devolver un XElement, porque todo lo que tiene que hacer es marcar propiedades con atributos y el serializador genera toda la generación XML para usted. (Además obtendrá deserialización gratis, suponiendo que tiene un constructor sin parámetros y, bueno, un montón de otras cosas.)

Por otro lado, a) tiene que crear un XmlSerializer para hacerlo, b) tratar con las propiedades de la colección no son del todo obvias, es posible que te guste, y c) la serialización XML es bastante tonta; no tienes suerte si quieres hacer algo elegante con el XML que estás generando.

En muchos casos, esos problemas no importan ni un poco. Por mi parte, prefiero marcar mis propiedades con atributos que escribir un método.

4

Usted puede devolver un XmlDocument para el método ToXML en su clase, a continuación, cuando se va a añadir el elemento con el documento resultado justo usar algo como:

XmlDocument returnedDocument = Your_Class.ToXML(); 

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

De esta manera todo el valor de "Desired_Element_Name "en su resultado XmlDocument será todo el contenido del Documento devuelto.

Espero que esto ayude.

4

Crear un nuevo XmlDocument con el contenido que desea y luego importarlo a un documento existente, mediante el acceso a la propiedad ownerDocument de sus nodos existentes:

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); 

Buena suerte.

2

Otra opción es pasar un delegado al método, que creará un XmlElement. De esta forma, el método de destino no tendrá acceso a todo XmlDocument, pero podrá crear nuevos elementos.

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

XmlNodes vienen con una propiedad OwnerDocument.

Tal vez sólo se puede hacer:

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

por qué no considerar la creación de su clase de datos (es) simplemente como un XmlDocument con subclases, entonces usted consigue todo esto de forma gratuita. No necesita serializar o crear ningún nodo fuera de documento, y obtiene la estructura que desea.

Si desea hacerlo más sofisticado, escriba una clase base que sea una subclase de XmlDocument, luego dele accesos básicos, y ya está configurado.

Aquí es un tipo genérico que arme para un proyecto ...

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 

    } 
}