1

我正在使用 XSD 文件通过 xsd.exe 生成 C# 代码,并使用生成的代码序列化 XML。

问题是我必须生成一个具有默认/根 xmlns 名称空间的 XML 文件,以及在该元素上定义另一个“原始”名称空间的元素(不带前缀)。

这是一个简单的示例(不是整个 XML),用于说明目的:

<?xml version="1.0" encoding="utf-16"?>
<Request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://mynamespace">
   <ABC>STACKOVERFLOW</ABC>
   <Method Name="TEST" xmlns="http://myothernamespace">
     <DEF>123456</DEF>
   </Method>
 </Request>

我找不到在 Method 元素上“插入”第二个 xmlns 属性的方法(没有前缀)。

问题是我不能使用前缀,因为这个 XML 被发送到另一端没有使用 XML 反序列化器的应用程序,但是它将 XML 解析为字符串并寻找精确的字符串匹配(当然这不是可以更改此“接收”应用程序)。

我在 XSD 中尝试了很多不同的东西,但没有任何效果 :( 我正在使用 MSMQ 传输 XML 消息,所以我没有自己序列化请求(只是为请求对象提供正确的参数,并且序列化在幕后处理)。

我可以使用 XmlSerializer 在没有第二个 xmlns 的情况下将 XML 序列化为字符串,并在发送之前通过编码将其添加到 XML(到字符串)中,但这很脏,并且让我走这条路只是为了添加这个“ Method 元素上的 xmlns" 属性。

有没有办法通过纯XSD(通过使用xsd.exe的.NET代码生成)来解决这个问题?

希望我的问题很清楚,希望有人能够回答

提前致谢 !

4

2 回答 2

2

像这样定义你的类型:

[XmlRoot("Method")]
public class MyMethod
{
    [XmlAttribute]
    public String Name   { get; set; }
    [XmlElement]
    public int DEF   { get; set; }
}


[XmlRoot("Request", Namespace="http://mynamespace")]
public class MyRequest
{
    [XmlElement]
    public String ABC { get; set; }

    [XmlElement(Namespace="http://myothernamespace")]
    public MyMethod Method { get; set; }
}

支持代码:

    static TextWriter GetWriter(bool wantSave)
    {
        if (wantSave)
        {
            var fs = new FileStream(StorageFile, FileMode.Create);
            return new StreamWriter(fs, new UTF8Encoding());
        }
        return Console.Out;
    }

    private static void ShoworSave(MyRequest r, bool wantSave)
    {
        if (r==null)
        {
            Console.WriteLine(" --null--");
            return;
        }

        Console.WriteLine("\n");

        var writerSettings = new XmlWriterSettings
        {
            OmitXmlDeclaration = true,
            Indent = true
        };

        using (XmlWriter xmlWriter =
               XmlWriter.Create(GetWriter(wantSave), writerSettings))
        {
            XmlSerializer ser = new XmlSerializer(r.GetType());
            var ns = new XmlSerializerNamespaces();
            ns.Add("", "http://mynamespace"); // default xmlns
            ser.Serialize(xmlWriter, r, ns);
        }
        Console.WriteLine("\n");
    }

然后像这样使用:

        var request = new MyRequest
        {
            ABC = "HelloWorld",
            Method = new MyMethod
            {
                Name="TEST",
                DEF=123456
            }
        };

        SaveOrShow(request, false);

结果:

<Request xmlns="http://mynamespace">
  <ABC>HelloWorld</ABC>
  <Method Name="TEST" xmlns="http://myothernamespace">
    <DEF>123456</DEF>
  </Method>
</Request>

讨论

Xml 序列化程序允许您指定命名空间映射,从而可以为序列化输出提供命名空间前缀列表和它们映射到的实际命名空间。要设置默认的 xml 命名空间及其前缀,请使用前缀“”(空字符串)。

所以我使用的代码指定了默认命名空间。

我还使用适当的 xml 序列化程序属性修饰了各种类型和成员,以获得正确的命名空间。

乍一看,您可能认为在代码中的几个不同位置使用 xml 命名空间字符串(在您的示例中为“http://mynamespace”)违反了干净编码的“不要重复自己”格言。但这不是真的。在我使用它的一个地方,它设置了该类型的 XML 命名空间。另一方面,它为序列化程序指定了默认的 xml 命名空间。

如果我没有指定后者,那么你会得到一个前缀;这将为您提供语义上等效的 xml 信息集,但是因为您说您的接收器应用程序不是真正的 xml 感知的,所以它会破坏该接收器。

此外,关于您关于 xsd.exe 和代码生成的问题,不确定您在做什么,但请考虑一下:Xsd.exe 只是一个工具。获取该工具的输出并对其进行调整、编辑并没有错。如果您的类型相对简单,您可能会发现只在 C# 中定义类型会更容易,正如我在上面所展示的。如果您很喜欢将 XSD 作为源代码,那么您将需要依赖 xsd.exe 工具。在这种情况下,您需要在 xsd 文档的适当位置指定这些名称空间。您选择哪个选项取决于您。

于 2012-06-14T23:42:04.873 回答
1

不,如果你xmlns="http://myothernamespace">说你的根节点所有无前缀的节点都将被视为在该命名空间中,那是有效的。上述格式称为默认命名空间。您必须添加更多代码才能在 Xpath 中使用它。

例如

在你的例子SomeNode.SelectSingleNode("Test")中不起作用。

您需要手动将命名空间添加到 NameSpaceManager 的实例中,并使用需要管理器的重载。

例如

<Test>

<Test xmlns="http://myothernamespace">

是不同的动物。

为了使其正确序列化,您可以实例化一个 XmlSerialiserNamespaces,并为其添加前缀“”,然后将其传递给序列化器。

这里的上一个问题,它在 VB.Net 中,但你会明白的。

序列化和默认命名空间

于 2012-06-14T23:22:00.567 回答