最近做一个Mobile项目,服务端采用java Xfire的WebService, 客户端为Windows Mobile, Mobile 程序使用 WCF 客户端, 测试环境服务器为Tomcat, 一切正常,迁移到WebLogic 10 出现一个奇怪的问题。
形如下面的服务方法:
public User GetUser();
其中User为一个自定义引用对象。
当服务返回空 (null )时, 使用 NetCFSvcUtil 生成代码调用此方法时,返回的 User 并不为 null, 居然是一个使用默认构造函数构造的 User 对象(所有属性为默认值),最后通过调式代码发现是客户端在处理Xml 中的 Nillable 属性时存在缺陷。
以下是响应的Soap消息体(消息头的命名空间定义省略):
<GetUserResonse>
<out>
<User xsi:nil="true"></User>
</out>
</GetUserResonse>
这种格式的XML 使用 XmlSerializer 类进行反序列化会产生非空的 User 对象。
而下面这种格式就会产生null对象(正确的解析):
<GetUserResonse>
<out>
<User xsi:nil="true" />
</out>
</GetUserResonse>
我就纳闷了,<User xsi:nil="true"></User>和<User xsi:nil="true" />只是表示形式不同,但是XmlSerializer反序列化会产生不同的结果,不知哪位大拿能够给小弟解惑。
目前的解决方案,在CFContractSerializer中添加FormatNillable方法:
{
MemoryStream ms = new MemoryStream();
XmlDocument xd = new XmlDocument();
xd.Load(reader);
XmlNamespaceManager manager = new XmlNamespaceManager(xd.NameTable);
manager.AddNamespace("nn", "http://www.w3.org/2001/XMLSchema-instance");
XmlNodeList list = xd.SelectNodes("//*[@nn:nil = 'true']", manager);
foreach (XmlElement element in list)
{
element.IsEmpty = true;
}
xd.Save(ms);
ms.Position = 0;
return ms;
}
修改CFContractSerializer的ReadObject方法:
{
if ((verifyObjectName == false))
{
throw new System.NotSupportedException();
}
if (this.info.IsWrapped)
{
// Some WSDLs incorrectly advertise their response message namespaces.
// Attempt to interop with these by coercing our expected namespace to match.
if ((this.serializer.CanDeserialize(reader) == false))
{
this.createSerializer(new System.Xml.XmlQualifiedName(System.Xml.XmlConvert.DecodeName(reader.LocalName), reader.NamespaceURI));
}
using (Stream stream = this.FormatNillable(reader))
{
return this.serializer.Deserialize(stream);
}
}
else
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
settings.OmitXmlDeclaration = true;
System.Xml.XmlWriter innerWriter = System.Xml.XmlDictionaryWriter.Create(ms, settings);
innerWriter.WriteStartElement(artificialWrapper.Name, artificialWrapper.Namespace);
string[] commonPrefixes = new string[] {
"xsi",
"xsd"};
for (int i = 0; (i < commonPrefixes.Length); i = (i + 1))
{
string ns = reader.LookupNamespace(commonPrefixes[i]);
if ((ns != null))
{
innerWriter.WriteAttributeString("xmlns", commonPrefixes[i], null, ns);
}
}
for (
; ((reader.NodeType == System.Xml.XmlNodeType.EndElement)
== false);
)
{
innerWriter.WriteNode(reader, false);
}
innerWriter.WriteEndElement();
innerWriter.Close();
ms.Position = 0;
using (System.Xml.XmlReader innerReader = System.Xml.XmlDictionaryReader.Create(ms))
{
using (Stream stream = this.FormatNillable(innerReader))
{
object obj = this.serializer.Deserialize(stream);
return obj;
}
}
}
}
通过手工格式化这个XML可以处理此问题,但是这里还是恳请高手赐教,这是WCF的BUG还是我对这个问题理解有误?