Reputation: 3722
I am using an XSD file to generate C# code through xsd.exe and serializing XML using the resulting code.
The problem is that I must generate an XML file having a defaut/root xmlns namespace, and an element definining another "raw" namespace on this element (without prefix).
Here is a light sample (not the whole XML), for illustration purposes :
<?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>
I can't find a way to "insert" the second xmlns attribute as such on the Method element (without prefix).
Thing is that I cannot use prefix, as this XML is sent to an application that is not using an XML deserializer on the other end, but which is parsing the XML as a string and looking for exact string match (and of course this is not possible to change this "receiving" application).
I have tried a lot of different things in XSD but nothing works :( I am using MSMQ to transfer the XML message so I am not serializing the Request by myself (just giving correct parameters to the request object and the serialization is handled behind the scene).
I could use an XmlSerializer to serialize the XML to a string without the second xmlns, and add it via coding to the XML (to the string) before sending it, but this is dirty and bugs me to go this way just to add this "xmlns" attribute on Method element.
Is there any way to go arround this via pure XSD (through .NET code generation using xsd.exe) or not ?
Hope my question is clear and hope some people will be able to answer it
Thanks in advance !
Upvotes: 1
Views: 1682
Reputation: 192417
Define your types like this:
[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; }
}
Supporting code:
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");
}
And then use like this:
var request = new MyRequest
{
ABC = "HelloWorld",
Method = new MyMethod
{
Name="TEST",
DEF=123456
}
};
SaveOrShow(request, false);
results:
<Request xmlns="http://mynamespace">
<ABC>HelloWorld</ABC>
<Method Name="TEST" xmlns="http://myothernamespace">
<DEF>123456</DEF>
</Method>
</Request>
The Xml serializer allows you to specify a namespace map, whereby you can give a list of namespace prefixes and the actual namespaces they map to, for the serialized output. To set the default xml namespace and its prefix, use the prefix of "" (empty string).
So the code I use specifies that default namespace.
I have also decorated the various types and members with the appropriate xml serializer attributes to get the right namespaces.
At first glance, you may think that the use of the xml namespace string (in your example of "http://mynamespace") in several distinct places in the code is a violation of "Dont repeat yourself" aphorism of clean coding. But this is not really true. In the one place I use it, it sets the XML namespace of the type. In the other place, it specifies the default xml namespace for the serializer.
If I did not specify the latter then you would get a prefix; this would give you the semantically equivalent xml infoset, but because you said that your receiver application isn't truly xml-aware, it would break that receiver.
Also, regarding your question about xsd.exe and code generation, not sure what you're getting at but consider this: Xsd.exe is just a tool. There's nothing wrong with taking the output of that tool and tweaking it, editing it. If your types are relatively simple you may find it easier to just define the types in C#, as I have shown above. If you are heavy into XSD as source code, then you will want to rely on the xsd.exe tool. In that case you need to specify those namespaces in the proper places in the xsd document. Which option you choose is up to you.
Upvotes: 2
Reputation: 20320
No that's valid if you put xmlns="http://myothernamespace">
on say your root node all unprefixed nodes will be treated as in that namespace. The above format is called the default namespace. You do have to add a bit more code to use it in say Xpath.
e.g.
in your example SomeNode.SelectSingleNode("Test")
won't work.
you need to manually add your namespace to an instance of NameSpaceManager, and use the overload that takes a manager.
e.g
<Test>
and
<Test xmlns="http://myothernamespace">
are different animals.
In terms of getting it serialised correctly you instance an XmlSerialiserNamespaces add it with a prefix of "", and then pass that to the serialiser.
Previous question here, it's in VB.Net, but you'll get the idea.
Serialisation and default namespace
Upvotes: 1