Подтвердить что ты не робот

Создание XmlNode/XmlElement в С# без XmlDocument?

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Настроить XML-сериализацию XML-объекта с атрибутами .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

Я бы рекомендовал использовать 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;
}

Ответ 3

Из W3C спецификация Document Object Model (Core) уровня 1 (жирный мой):

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

Удачи.

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

    }
}

Ответ 10

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

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

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

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

Ответ 11

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