【问题标题】:How to create XML with multiple namespace attributes in C#如何在 C# 中创建具有多个命名空间属性的 XML
【发布时间】:2013-02-20 23:35:12
【问题描述】:

例如,我如何在 C# 中生成这个 XML

<?xml version='1.0'?>
<oneshot xmlns='http://www.w3.org/2002/xforms' xmlns:dm='http://mobileforms.foo.com/xforms' xmlns:h='http://www.w3.org/1999/xhtml' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
  <dm:form_namespace>Foo</dm:form_namespace>
  <Days>6</Days>
  <Leave_Type>Option 3</Leave_Type>
</oneshot>

我特别纠结于 xmlns:dm 声明。有什么想法吗?

【问题讨论】:

  • “序列化”为“需要创建在使用 XML 序列化时生成给定 XML 的类”或只是“如何使用不同命名空间中的节点创建 XML”?
  • 你能发布oneshot对象吗?
  • @AlexeiLevenkov 公平通话。我更新了标题,因为这在技术上不是序列化。

标签: c# .net xml serialization


【解决方案1】:

您最好的选择(阅读:最少的 hack)可能是自定义 IXmlSerializable 实现;您可以通过XmlRootAttributeXmlElementAttribute 等的组合部分获得您想要的结果,如下所示:

[Serializable]
[XmlRoot("oneshot")]
public class OneShot 
{
    [XmlElement("form_namespace", Namespace="http://mobileforms.foo.com/xforms")]
    public string FormNamespace {get; set;}
    [XmlElement("Days")]
    public int Days {get; set;}
    [XmlElement("Leave_Type")]
    public string LeaveType {get; set;}

这会产生类似的东西:

<?xml version="1.0" encoding="utf-16"?>
<oneshot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <form_namespace xmlns="http://mobileforms.foo.com/xforms">Foo</form_namespace>
  <Days>6</Days>
  <Leave_Type>Option 3</Leave_Type>
</oneshot>

但是如果你实现IXmlSerializable,你就可以完全控制:

public class OneShot : IXmlSerializable
{
    public string FormNamespace {get; set;}
    public int Days {get; set;}
    public string LeaveType {get; set;}

    #region IXmlSerializable
    public void WriteXml (XmlWriter writer)
    {
        writer.WriteStartElement("oneshot");
        writer.WriteAttributeString("xmlns", null, "http://www.w3.org/2002/xforms");
        writer.WriteAttributeString("xmlns:dm", null, "http://mobileforms.foo.com/xforms");
        writer.WriteAttributeString("xmlns:h", null, "http://www.w3.org/1999/xhtml");
        writer.WriteAttributeString("xmlns:xsd", null, "http://www.w3.org/2001/XMLSchema");
        writer.WriteElementString("dm:form_namespace", null, FormNamespace);
        writer.WriteElementString("Days", Days.ToString());
        writer.WriteElementString("Leave_Type", LeaveType);
        writer.WriteEndElement();
    }

    public void ReadXml (XmlReader reader)
    {
        // populate from xml blob
    }

    public XmlSchema GetSchema()
    {
        return(null);
    }
    #endregion
}

这给了你:

<?xml version="1.0" encoding="utf-16"?>
<OneShot>
  <oneshot xmlns="http://www.w3.org/2002/xforms" xmlns:dm="http://mobileforms.foo.com/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <dm:form_namespace>Foo</dm:form_namespace>
    <Days>6</Days>
    <Leave_Type>Option 3</Leave_Type>
  </oneshot>
</OneShot>

【讨论】:

  • 一切看起来都非常有希望。但是,在实践中,我在编写第一个属性字符串时遇到异常“前缀''不能在同一个起始元素标记内从''重新定义为'w3.org/2002/xforms'。”然后我在第二个属性字符串上得到一个“'xmlns:dm' 中的名称字符无效。':' 字符,十六进制值 0x3A,不能包含在名称中。”
  • @MattDell 在您的问题中添加您要尝试的内容?听起来可能是使用问题...
  • @MattDell,考虑使用 WriteElementString 的 4 参数版本,它采用命名空间前缀(我将发布示例作为单独的答案,但如果你喜欢它,应该去这个......)
  • @JerKimball 我正在尝试使用此 API:docs.devicemagic.com/push-api 我似乎无法生成有效的 XML 文档来发布到它。
  • @MattDell 我的意思是,添加您的 WriteXml 版本 - 很有可能,有更好/不同的覆盖可以使用(正如 Alexi 提到的)
【解决方案2】:

使用不同命名空间中的节点编写 XML 的一种方法是使用 XmlWriter.WriteElementString 的 4 参数版本以您想要的方式显式指定命名空间和前缀:

var s = new StringWriter();
using (var writer = XmlWriter.Create(s))
{
    writer.WriteStartElement("oneshot", "http://www.w3.org/2002/xforms");
    writer.WriteElementString("dm", "form_namespace", 
         "http://mobileforms.foo.com/xforms","Foo");
    // pick "http://www.w3.org/2002/xforms" by default for Days node
    writer.WriteElementString("Days", "6");
    // or you can explicitly specify "http://www.w3.org/2002/xforms"
    writer.WriteElementString("Leave_Type", 
         "http://www.w3.org/2002/xforms", "Option 3");
    writer.WriteEndElement();
}

Console.Write(s.ToString());

请注意,您的示例 XML 定义了比 XML 中使用的更多的前缀。如果您的要求是生成“文本相同的 XML”(相对于从 XML 的角度来看相同,但不必用相同的文本表示),您可能需要付出更多的努力在您需要的地方添加名称空间前缀和 xmlns 属性。

注意 2:首先创建 XML 对象(XDocument 用于现代/LINQ 方式,或者XmlDocument,如果您更喜欢 DOM)可能是更简单的方法。

【讨论】:

    【解决方案3】:

    尝试使用 .NET 4.0+ 的程序集 System.Xaml 中的 XAML 序列化程序。

    您可能需要添加属性以将属性标记为内容而不是 XML 属性。

    【讨论】:

      【解决方案4】:

      谢谢大家的帮助!这是上述两个答案的组合,为我做到了。我将设置一个建议 IXmlSerializable 方法的方法,因为这是解决方案的主要部分。

      我只好在类名上声明XMLRoot标签,去掉WriteStartElement和WriteEndElement,然后用四参数声明。

      这是最终起作用的类:

      [Serializable]
      [XmlRoot("oneshot")]
      public class LeaveRequestPush : IXmlSerializable
      {
          public string FormNamespace { get; set; }
          public int Days { get; set; }
          public string LeaveType { get; set; }
      
          #region IXmlSerializable
      
          public void WriteXml(XmlWriter writer)
          {
              writer.WriteElementString("dm", "form_namespace", "http://mobileforms.devicemagic.com/xforms", FormNamespace);
              writer.WriteElementString("Days", Days.ToString());
              writer.WriteElementString("Leave_Type", LeaveType);
          }
      }
      
      public void ReadXml (XmlReader reader)
      {
          // populate from xml blob
      }
      
      public XmlSchema GetSchema()
      {
          return(null);
      }
      

      再次感谢大家的共同努力。我自己不会得到这个!

      【讨论】:

      • 多个命名空间呢?这不是你的问题吗?你是怎么解决的?
      【解决方案5】:

      这段代码可以解决问题!

      public void WriteXml(XmlWriter writer)
              {
                  const string ns1 = "http://firstline.com/";
                  const string xsi = "http://www.w3.org/2001/XMLSchema-instance";
                  writer.WriteStartElement("myRoot", ns1);
                  writer.WriteAttributeString("SchemaVersion", "1.0");
                  writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi);
                  writer.WriteAttributeString("xsi", "schemaLocation", xsi, ns1 + " schema1.xs");
      
              writer.WriteStartElement("element1", ns1);
              writer.WriteElementString("test1", ns1, "test value");
              writer.WriteElementString("test2", ns1, "value 2");
              writer.WriteEndElement();
              writer.WriteEndElement();//to close classname that has root xml
      }
      

      具有多个很棒的命名空间的 XML!

      <?xml version="1.0" encoding="utf-16"?>
      <myClassNameWhereIXmlSerializableIsImplemented>
        <myRoot SchemaVersion="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://firstline.com/ schema1.xs" xmlns="http://firstline.com/">
          <element1>
            <test1>test value</test1>
            <test2>value 2</test2>
          </element1>
        </myRoot>
      </myClassNameWhereIXmlSerializableIsImplemented>
      

      这条线有些混乱

      writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi);
      

      如果你给出一个随机的 url 而不是 "http://www.w3.org/2000/xmlns/" ,它将失败。出现在 xml 中的 url 实际上来自“xsi”变量。 再举一个例子来证明这一点

      writer.WriteAttributeString("xml", "base", "http://www.w3.org/XML/1998/namespace", base1);
      

      在哪里base1 = "&lt;custom url&gt;"

      如果你想用&lt;d:test1&gt;somevalue&lt;\d:test1&gt;这样的前缀输出,那么writer.WriteElementString("d","test1", ns1, "somevalue");如果上面没有定义ns1,那么它将被添加到xml输出中。

      但是对于 <d:test1> <blah>... <\d:test1> StartElement 需要 writer.WriteStartElement("test1", ns1); 以在需要时使用 writer.WriteEndElement(); 关闭

      【讨论】:

        猜你喜欢
        • 2011-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-30
        • 2013-06-09
        • 2014-08-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多