【问题标题】:Creating an XmlNode/XmlElement in C# without an XmlDocument?在没有 XmlDocument 的 C# 中创建 XmlNode/XmlElement?
【发布时间】:2008-10-18 20:07:15
【问题描述】:

我有一个简单的类,它基本上只包含一些值。我已经重写了ToString() 方法来返回一个不错的字符串表示。

现在,我想创建一个ToXml() 方法,它会返回如下内容:

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

当然,我可以在这里只使用StringBuilder,但我想返回XmlNodeXmlElement,与XmlDocument.AppendChild 一起使用。

除了调用XmlDocument.CreateElement之外,我似乎无法创建XmlElement,所以我想知道我是否忽略了任何东西,或者我是否真的必须传入XmlDocument或@987654331 @ 使用,还是让函数返回一个包含我想要的 XML 的字符串?

【问题讨论】:

  • 问题标题与问题内容/目标不对应。你想知道如何序列化你的类。我确实需要一个 XmlNode 实例来将其作为 webservice 参数传递。用户将从输入字符串创建 XmlNode。
  • @DaviFiamenghi - 您的评论不正确。如果有人选择使用 XmlNode 手动构建 XML 数据,这是他们的选择,似乎没有办法在 .Net 中创建此类 XmlNode 对象而不从 XmlDocument 创建它。

标签: c# xml


【解决方案1】:

我建议使用 System.Xml.Linq 的 XDoc 和 XElement 而不是 XmlDocument 的东西。这样会更好,您将能够利用 LINQ 的强大功能来查询和解析您的 XML:

使用 XElement,您的 ToXml() 方法将如下所示:

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

    return element;
}

【讨论】:

  • 这么简单!谢谢你。
【解决方案2】:

来自 W3C Document Object Model (Core) Level 1 规范(粗体是我的):

由此定义的大部分 API 规范是接口而不是 比上课。这意味着一个 实际实现只需要暴露 具有定义名称的方法和 指定的操作,实际上不是 实现对应的类 直接到接口。这 允许实现 DOM API 作为传统之上的薄薄的饰面 拥有自己数据的应用程序 结构,或在更新的之上 不同类别的应用 层次结构。 这也意味着 普通构造函数(在 Java 或 C++ 意义)不能用于创建 DOM 对象,因为底层 要构造的对象可能有 与 DOM 关系不大 接口。常规解决方案 面向对象设计中的这一点是 定义创建的工厂方法 实现对象的实例 各种接口。在 DOM 中 级别 1,对象实现一些 接口“X”是由一个 文档上的“createX()”方法 界面; 这是因为所有的 DOM 对象存在于一个上下文中 特定文档

AFAIK,除了XmlDocument 从构造函数之外,您不能创建任何XmlNodeXmlElement, XmlAttribute, XmlCDataSection 等)。

此外,请注意,您不能将XmlDocument.AppendChild() 用于不是通过 same 文档的工厂方法创建的节点。如果您有来自另一个文档的节点,则必须使用XmlDocument.ImportNode()

【讨论】:

    【解决方案3】:

    您可能想了解如何使用 .NET 的内置功能将对象序列化和反序列化为 XML,而不是在本质上只是一个数据传输对象的每个类上创建 ToXML() 方法。

    我已经在几个项目中成功使用了这些技术,但目前手头没有实现细节。稍后我会尝试用我自己的例子来更新我的答案。

    以下是 Google 返回的几个示例:

    .NET 中的 XML 序列化 by Venkat Subramaniam http://www.agiledeveloper.com/articles/XMLSerialization.pdf

    如何将对象序列化和反序列化为 XML http://www.dotnetfunda.com/articles/article98.aspx

    使用 .NET 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

    【讨论】:

    • 你能看到我的question 之一关于 xml 响应吗?
    【解决方案4】:

    XmlNode 带有 OwnerDocument 属性。

    也许你可以这样做:

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

    【讨论】:

      【解决方案5】:

      您可以在您的类中为ToXML 方法返回一个XmlDocument,然后当您要在结果文档中添加元素时,只需使用类似:

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

      这样,您的结果 XmlDocument 中“Desired_Element_Name”的整个值将是返回文档的全部内容。

      我希望这会有所帮助。

      【讨论】:

        【解决方案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。

          您可以使用 XElement 从头开始​​创建 XML - 这应该会让您很满意。

          【讨论】:

            【解决方案8】:

            为什么不考虑将您的数据类创建为一个 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
            
                }
            }
            

            【讨论】:

            • 这段代码在某些地方发生异常时有几个一次性对象泄漏。
            【解决方案9】:

            另一种选择是将委托传递给方法,该方法将创建一个 XmlElement。这样,目标方法将无法访问整个 XmlDocument,但可以创建新元素。

            【讨论】:

              【解决方案10】:

              您不能返回XmlElementXmlNode,因为这些对象始终且仅存在于拥有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.....
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-02-03
                  • 2011-01-23
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多