We return instances of the class AuthenticateUserOutput from a WCF web service.
So we have this method:
public override AuthenticateUserOutput AuthenticateUser(AuthenticateUserInput AuthenticateUserInput)
AuthenicateUserOutput is auto generated:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.5420")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="xxxx")]
public partial class AuthenticateUserOutput : WS2MethodOutput
{
private bool authenticationResultField;
private UserContext userContextField;
/// <remarks/>
public bool AuthenticationResult
{
get
{
return this.authenticationResultField;
}
set
{
this.authenticationResultField = value;
}
}
/// <remarks/>
public UserContext UserContext
{
get
{
return this.userContextField;
}
set
{
this.userContextField = value;
}
}
}
We need to be able to de serialize as AuthenticateUserOutput, but it’s not working.
As a test, I instantiated an AuthenticateUserOutput, serialized it and tried to de-serialize it.
It fails with InvalidOperationException.
Here’s the serialized Xml:
<?xml version="1.0" encoding="utf-16"?>
<AuthenticateUserOutput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="xxxx">
<Response xmlns="xxxx">
<IsValid>true</IsValid>
<Success>true</Success>
</Response>
<AuthenticationResult xmlns="xxxx">true</AuthenticationResult>
<UserContext xmlns="xxxx">
</UserContext>
</AuthenticateUserOutput>
Here’s the serialization and de-serialization code:
public static string ToXml(this object input)
{
string output;
XmlSerializer serializer = new XmlSerializer(input.GetType());
StringBuilder sb = new StringBuilder();
using (TextWriter textWriter = new StringWriter(sb))
{
serializer.Serialize(textWriter, input);
}
output = sb.ToString();
return output;
}
And:
public static T FromXml<T>(this string input)
{
T output;
XmlSerializer serializer = new XmlSerializer(typeof(T));
output = (T)serializer.Deserialize(new StringReader(input));
return output;
}
The exact details of the exception:
System.InvalidOperationException occurred
Message="<AuthenticateUserOutput xmlns='xxxx'> was not expected."
Source="qkxd8dd-"
StackTrace:
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderAuthenticateUserOutput.Read31_AuthenticateUserOutput()
InnerException:
So it can’t de-serialize it’s own serialized Xml.
Can anybody see why?
Thanks,
J1M.
UPDATE: Here is the test code:
AuthenticateUserOutput test = new AuthenticateUserOutput();
test.AuthenticationResult = true;
test.Response = new ResponseType();
test.Response.Exception = null;
test.Response.IsValid = true;
test.Response.Success = true;
test.Response.ValidationErrors = null;
test.UserContext = new UserContext();
string serializedXml = test.ToXml();
AuthenticateUserOutput deserializedString = serializedXml.FromXml<AuthenticateUserOutput>();
UPDATE2: Deserializer works thanks to Mark Gravell
OK, I forgot I’d added this to get the namespaces to come out correctly when de-serializing:
public partial class WS2MethodInput
{
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces _xmlns;
/// <summary>
/// Constructor for WS2MethodInput that sets up default namespaces
/// </summary>
public WS2MethodInput()
{
_xmlns = new XmlSerializerNamespaces();
_xmlns.Add("", "xxxx");
}
}
With this, the serialized Xml is as it was at the top of this message.
Without it, the de-serializer works but AuthenticateUserOutput is missing a namespace:
<?xml version="1.0" encoding="utf-16"?>
<AuthenticateUserOutput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="xxxx">
<Response xmlns="xxxx">
<IsValid>true</IsValid>
<Success>true</Success>
</Response>
<AuthenticationResult xmlns="xxxx">true</AuthenticationResult>
<UserContext xmlns="xxxx">
</UserContext>
</AuthenticateUserOutput>
Note the xmlns="xxxx" at the end of AuthenticateUserOutput
Problem is, now I can’t use that Xml with our other code without either:
1) Loading it into an XDocument and adding the namespace and removing it when I need to de-serialize it
2) Doing the same with string replace of regex or something
Neither of which I really like. In fact that’s horrible! 8X
The problem was setting the XmlSerializerNamespaces to force xmlns on the root element.
Take that away and it works.
However, now I can’t use the serialized Xml anywhere else because it’s missing a namespace.
I will start a new question that is focused on that.
Thanks,
J1M.
UPDATE:
OK, I figured it out so I’m not going to write a new question.
Given the following XSD:
svcutil generates something like this, although I imagine there are lots of tools that spit out something similar for that XSD:
Now, using the “default”
XmlSerializercode:You get the following Xml:
Which does NOT conform to the schema (validation fails, which is what is killing us at the moment)
OK, so, I found a reference to changing
[XmlType]to[XmlRoot]and a cursory test worked, if I change the generated class I now get the following conformant Xml:But I don’t want to hand change all the auto generated classes. So after some time I found the following solution:
As you can see, rather than just using the default constructor for XmlSerializer, you can create an
XmlAttributeOverridesand add to it oneXmlAttributeson which you instantiate theXmlRootattribute.In order to make this nice and generic, it hoovers the Namespace out of the incoming type with
((XmlTypeAttribute)incomingType.GetCustomAttributes(typeof(XmlTypeAttribute), true)[0]).Namespace, which fails if the type doesn’t have that attribute so this solution needs more work but you get the general idea.Regards,
J1M