views:

623

answers:

2

Hello everyone,

My confusion is, I am using .Net C# XMLSerializer to serialize a customize defined type, using the schema/cs file generated by XSD tool from an input original XML file. But the generated serialized XML file namespace is different from original XML input file. Especially from original XML file, Envelope belongs to namespace soapenv, but in the serialized XML file, Envelope belongs to default namespace. Any ideas what is wrong?

Here is how I use XSD tool to generate schema file and cs file for the customized defined type from XML input file XMLFile1.xml,

BTW: XML files can be downloaded from,

http://www.mediafire.com/?nwngwzz3gmm

D:\>xsd XMLFile1.xml
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.3038]
Copyright (C) Microsoft Corporation. All rights reserved.
Writing file 'D:\XMLFile1.xsd'.

D:\>xsd XMLFile1.xsd XMLFile1_app1.xsd /classes
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.3038]
Copyright (C) Microsoft Corporation. All rights reserved.
Writing file 'D:\XMLFile1_XMLFile1_app1.cs'.

Here is how I use C# code to serialize the file and output to TestOutputFile.xml,

static void Main(string[] args)
{
    Envelope en = new Envelope();
    en.Items = new EnvelopeBody[1];
    en.Items[0] = new EnvelopeBody();
    en.Items[0].QueryResponse = new QueryResponseFaculties[1];
    en.Items[0].QueryResponse[0] = new QueryResponseFaculties();
    en.Items[0].QueryResponse[0].Name = "John";

    XmlSerializer serializer =
     new XmlSerializer(typeof(Envelope));
    TextWriter writer = new StreamWriter("TestOutputFile.xml");

    serializer.Serialize(writer, en);
    writer.Close();

    return;
}

The original XML file is,

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  <soapenv:Body>
    <QueryResponse xmlns="http://schemas.mycorp.com/test"&gt;
      <Faculties>
        <Name>
          John
        </Name>
      </Faculties>
    </QueryResponse>
  </soapenv:Body>
</soapenv:Envelope>

The serialized XML file is,

<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  <Body>
    <QueryResponse xmlns="http://schemas.mycorp.com/test"&gt;
      <Faculties>
        <Name>John</Name>
      </Faculties>
    </QueryResponse>
  </Body>
</Envelope>

EDIT 1:

Current code is,

static void Main(string[] args)
{
    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    ns.Add("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
    ns.Add("", "http://schemas.mycorp.com/test");
    ns.Add("xsd", "http://www.w3.org/2001/XMLSchema");
    ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");

    Envelope en = new Envelope();
    en.Items = new EnvelopeBody[1];
    en.Items[0] = new EnvelopeBody();
    en.Items[0].QueryResponse = new QueryResponseFaculties[1];
    en.Items[0].QueryResponse[0] = new QueryResponseFaculties();
    en.Items[0].QueryResponse[0].Name = "John";

    XmlSerializer serializer =
     new XmlSerializer(typeof(Envelope));
    TextWriter writer = new StreamWriter("TestOutputFile.xml");

    serializer.Serialize(writer, en, ns);
    writer.Close();

    return;
}

Current output (serialized XML file) is,

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://schemas.mycorp.com/test"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  <soapenv:Body>
    <QueryResponse>
      <Faculties>
        <Name>John</Name>
      </Faculties>
    </QueryResponse>
  </soapenv:Body>
</soapenv:Envelope>

I want the output to be (notice the location of string xmlns="http://schemas.mycorp.com/test"),

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  <soapenv:Body>
    <QueryResponse xmlns="http://schemas.mycorp.com/test"&gt;
      <Faculties>
        <Name>John</Name>
      </Faculties>
    </QueryResponse>
  </soapenv:Body>
</soapenv:Envelope>

thanks in advance, George

+2  A: 

Aren't they interchangeable? In one it is using xmlns to set the namespace at the element, and in the other a xmlns:soapenv alias - but the meaning is the same, and IMO the second version is cleaner.

There is the XmlSerializerNamespaces class that can fix this ; full example:

using System;
using System.Xml.Serialization;
[XmlRoot(Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope {
    public Body Body { get; set; }
}
public class Body {
    [XmlElement(Namespace="http://schemas.mycorp.com/test")]
    public QueryResponse QueryResponse { get; set; }
}
public class QueryResponse {
    public Faculties Faculties { get; set; }
}
public class Faculties {
    public string Name { get; set; }
}
static class Program {
    static void Main() {
        XmlSerializer ser = new XmlSerializer(typeof(Envelope));
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
        ns.Add("xsd", "http://www.w3.org/2001/XMLSchema");
        ns.Add("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
        Envelope env = new Envelope {
            Body = new Body {
                QueryResponse = new QueryResponse {
                     Faculties = new Faculties { Name = "John"}
                }
            }
        };
        ser.Serialize(Console.Out, env, ns);
    }
}

Output (@encoding is because of Console.Out):

<?xml version="1.0" encoding="ibm850"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  <soapenv:Body>
    <QueryResponse xmlns="http://schemas.mycorp.com/test"&gt;
      <Faculties>
        <Name>John</Name>
      </Faculties>
    </QueryResponse>
  </soapenv:Body>
</soapenv:Envelope>
Marc Gravell
Hi Marc, I agree from XML syntax, they are the same. But for the limitation of my legacy program, I have to put Envelope into namespace soapenv. Any ideas how to resolve this issue?
George2
Thanks Marc, I have tried your solution, only one minor issue left, which is I want to define default namespace xmlns="schemas.mycorp.com/test from element QueryResponse, and not from the top level element Envelope, any solutions?
George2
Your solution will define all namespace at top level XML element of the serialized output XML file.
George2
Just add [XmlElement(Namespace="schemas.mycorp.com/test")] on the property you want to be in a different (default) namespace.
Marc Gravell
Sorry Marc, I think I did not make myself understood or I misunderstand your points. I think your solution did not solve my issue. Please refers to EDIT 1 section of my original post, which contains the current result of serialized file and my expected output serialized file. Any solutions?
George2
@George2: you should file a bug report against the legacy system, reminding them that they should support XML. What they currently support looks something like XML, but does not support basic standards.
John Saunders
Hi John, I fully agree. But there are some non-technical reasons. For my issue mentioned in EDIT 1 section, do you have any ideas to solve it?
George2
We've already said... use XmlSerializerNamespaces and mark the property... I can try to do an example, but...
Marc Gravell
Hi Marc, your solution of using XmlSerializerNamespaces will make namespace displays at the top of the serialized XML document, but what I want is to generate namespace definition from non-top level XML document, please refers to EDIT 1 section of non-top XML element QueryResponse, I want the namespace definition displays along with QueryResponse, but in the serialized output, the namespace displays at top XML element level, i.e. Envelope. Any solutions?
George2
+3  A: 

You need to use the XmlSerializerNamespaces class to set up your namespaces, then when serializing you need to pass that instance of XmlSerializerNamespces in along with your object. You will also need to set the namespace of your classes and/or properties using the XmlRoot and XmlElement attributes:

static void Main(string[] args)
{
    Envelope en = new Envelope();
    en.Items = new EnvelopeBody[1];
    en.Items[0] = new EnvelopeBody();
    en.Items[0].QueryResponse = new QueryResponseFaculties[1];
    en.Items[0].QueryResponse[0] = new QueryResponseFaculties();
    en.Items[0].QueryResponse[0].Name = "John";

    XmlSerializer serializer =
     new XmlSerializer(typeof(Envelope));
    TextWriter writer = new StreamWriter("TestOutputFile.xml");

    XmlSerlializerNamespaces xsn = new XmlSerializerNamespaces();
    xsn.Add("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");

    serializer.Serialize(writer, en, xsn);
    writer.Close();

    return;
}

[XmlRoot(Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
class Envelope
{
    // ...
}
jrista
Thanks jrista, I have tried your solution, only one minor issue left, which is I want to define default namespace xmlns="http://schemas.mycorp.com/test from element QueryResponse, and not from the top level element Envelope, any solutions?
George2
Your solution will define all namespace at top level XML element of the serialized output XML file.
George2
Just use the XmlElement attribute to set the namespace for any property individually.
jrista
Sorry jrista, I think I did not make myself understood or I misunderstand your points. I think your solution did not solve my issue. Please refers to EDIT 1 section of my original post, which contains the current result of serialized file and my expected output serialized file. Any solutions?
George2