XSD到XML。多个没有前缀的名称空间。如何?

时间:2012-06-14 22:49:19

标签: c# xml xsd xml-serialization xml-namespaces

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

问题在于我必须生成一个具有defaut / root 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将XML序列化为没有第二个xmlns的字符串,并在发送之前通过编码将其添加到XML(到字符串),但这很脏并且让我这样做只是为了在Method元素上添加此“xmlns”属性。

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

希望我的问题很明确,希望有些人能够回答它

提前致谢!

2 个答案:

答案 0 :(得分: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文档中的适当位置指定这些名称空间。您选择哪个选项取决于您。

答案 1 :(得分:1)

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

e.g。

您的示例SomeNode.SelectSingleNode("Test")中的

将无效。

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

e.g

<Test>

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

是不同的动物。

在正确序列化您的实例时,XmlSerialiserNamespaces将其添加前缀为“”,然后将其传递给序列化器。

以前的问题,这是在VB.Net,但你会明白的。

Serialisation and default namespace