2008-10-18 2 views
43

У меня есть простой класс, который по существу просто содержит некоторые значения. Я переопределил метод ToString(), чтобы вернуть красивое строковое представление.Создание XmlNode/XmlElement в C# без XmlDocument?

Теперь я хочу, чтобы создать метод ToXml(), который будет возвращать что-то вроде этого:

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

Конечно, я мог бы просто использовать StringBuilder здесь, но я хотел бы возвращать XmlNode или XmlElement, для использования с XmlDocument.AppendChild.

Я, кажется, не быть в состоянии создать XmlElement кроме вызова XmlDocument.CreateElement, поэтому мне интересно, если я просто игнорировать все, или если я действительно должны либо пройти в либо XmlDocument или ref XmlElement работать, или иметь функция возвращает строку, содержащую XML, который я хочу?

+0

название вопрос не соответствует вопросу содержания/цели. Вы хотите знать, как сериализовать ваши классы. Мне нужен экземпляр XmlNode, чтобы передать его как параметр webservice. Пользователь создаст XmlNode из строки ввода. – 2011-12-14 19:47:16

ответ

13

Вы можете хотите посмотреть, как вы можете использовать встроенные функции .NET для сериализации и десериализации объекта в XML, вместо того, чтобы создавать метод ToXML() для каждого класса, который по существу является объектом передачи данных.

Я успешно использовал эти методы на нескольких проектах, но сейчас у меня нет данных о реализации. Я попытаюсь обновить свой ответ своими примерами позже.

Вот несколько примеров, которые Google вернулся:

XML сериализации в .NET Venkat Субраманиам http://www.agiledeveloper.com/articles/XMLSerialization.pdf

Как сериализации и десериализации объекта в XML http://www.dotnetfunda.com/articles/article98.aspx

Настройка вашего .NET XML объекта сериализация с атрибутами XML XML http://blogs.microsoft.co.il/blogs/rotemb/archive/2008/07/27/customize-your-net-object-xml-serialization-with-net-xml-attributes.aspx

2

Вам нужно Linq - System.Xml.Linq, если быть точным.

Вы можете создавать XML с помощью XElement с нуля - это должно в значительной степени избавить вас от необходимости.

15

От W3C Document Object Model (Core) Level 1 спецификации (жирный шрифт мой):

Большинство API-интерфейсов, определенных этим спецификации являются интерфейсами, а , чем классы. Это означает, что фактическая реализация должна раскрывать только методы с определенными именами и указанную операцию, а не фактически реализовать классы, соответствующие непосредственно на интерфейсы. Это позволяет DOM API, которые будут реализованы в виде тонкой фанеры на вершине наследия приложений с их собственными данными структуры, или на вершине новых приложений с различным классом иерархии. Это также означает, что обычные конструкторы (в Java или C смысл ++) не может быть использован для создания объектов DOM, так как основные объекты, которые будут построены может иметь мало отношения к DOM интерфейсов. Обычное решение этому объектно-ориентированному дизайну составляет , чтобы определить фабричные методы, которые создают экземпляры объектов, которые реализуют различные интерфейсы . В Уровне 1 DOM объекты, реализующие некоторый интерфейс «X», создаются методом «createX()» на интерфейсе Document ; это потому, что все объекты DOM живут в контексте конкретных документов.

AFAIK, вы не можете создать любой XmlNode (XmlElement, XmlAttribute, XmlCDataSection, и т.д.), за исключением XmlDocument из конструктора.

Кроме того, обратите внимание, что вы не можете использовать XmlDocument.AppendChild() для узлов, которые не создаются с помощью заводских методов того же документа. Если у вас есть узел из другого документа, вы должны использовать XmlDocument.ImportNode().

40

Я бы рекомендовал использовать XDoc и XElement of System.Xml.Linq вместо XmlDocument. Это было бы лучше, и вы будете иметь возможность использовать власть LINQ в запросе и синтаксического анализа XML:

Использование XElement, ваш ToXml() метод будет выглядеть следующим образом:

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

    return element; 
} 
1

Вы не можете вернуть XmlElement или XmlNode, потому что эти объекты всегда существуют и существуют только в контексте владения XmlDocument.

Сериализация XML немного проще, чем возвращение XElement, поскольку все, что вам нужно сделать, это пометить свойства атрибутами, а сериализатор выполняет все XML-генерации для вас. (Кроме того, вы получаете бесплатную десерилизацию, предполагая, что у вас есть беспараметрический конструктор и, ну, куча других вещей.)

С другой стороны, a) вам нужно создать XmlSerializer, чтобы сделать это, b) имея дело с свойства коллекции не совсем понятны, как вам может показаться, и c) XML-сериализация довольно тупые; вам не повезло, если вы хотите сделать что-нибудь интересное с XML, который вы создаете.

Во многих случаях эти проблемы не имеют значения. Я бы предпочел пометить мои свойства атрибутами, а не написать метод.

4

Вы можете вернуть XmlDocument для метода ToXML в своем классе, а затем, когда вы собираетесь добавить элемент с результатом документа просто использовать что-то вроде:

XmlDocument returnedDocument = Your_Class.ToXML(); 

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

Таким образом, все значения для «Desired_Element_Name «на ваш результат XmlDocument будет весь контент возвращенного документа.

Надеюсь, это поможет.

4

Создать новый XmlDocument с содержанием вы хотите, и затем импортировать его в существующий документ, путем доступа к OwnerDocument свойство существующих узлов:

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

удачи.

2

Другой вариант - передать делегат методу, который создаст элемент XmlElement. Таким образом, целевой метод не получит доступ ко всему XmlDocument, но сможет создавать новые элементы.

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

XmlNodes имеют свойство OwnerDocument.

Может быть, вы можете просто сделать:

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

Почему бы не подумать о создании своего класса данных (ы) как просто подклассы XmlDocument, то вы получите все это бесплатно. Вам не нужно сериализовывать или создавать какие-либо внедомовые узлы вообще, и вы получаете нужную структуру.

Если вы хотите сделать его более сложным, напишите базовый класс, который является подклассом XmlDocument, затем дайте ему базовые аксессоры, и вы настроены.

Вот общий тип я соединял для проекта ...

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 

    } 
} 
Смежные вопросы