Создание XmlNode / XmlElement в C # без XmlDocument? - PullRequest
46 голосов
/ 19 октября 2008

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

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

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

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

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

Ответы [ 11 ]

42 голосов
/ 19 октября 2008

Я бы рекомендовал использовать XDoc и XElement из 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;
}
16 голосов
/ 19 октября 2008

Из W3C Объектная модель документа (ядро) Уровень 1 Спецификация (жирный шрифт):

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

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

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

14 голосов
/ 19 октября 2008

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

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

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

Сериализация XML в .NET, автор Venkat Subramaniam http://www.agiledeveloper.com/articles/XMLSerialization.pdf

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

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

11 голосов
/ 26 октября 2012

XmlNodes поставляются со свойством OwnerDocument.

Возможно, вы можете просто сделать:

//Node is an XmlNode pulled from an XmlDocument
XmlElement e = node.OwnerDocument.CreateElement("MyNewElement");
e.InnerText = "Some value";
node.AppendChild(e);
4 голосов
/ 16 февраля 2012

Создайте новый 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);

Удачи.

4 голосов
/ 28 января 2010

Вы можете вернуть 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 будет всем содержимым возвращенного документа.

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

3 голосов
/ 10 октября 2013

Почему бы не подумать о том, чтобы создать класс (ы) данных как подкласс 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

    }
}
2 голосов
/ 09 апреля 2012

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

2 голосов
/ 19 октября 2008

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

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

1 голос
/ 19 октября 2008

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...