问题
I'm using C# to read data from a Java-webservice.
I've created a Service reference in VS2008 to the server, and can call the one method that is there. However, a few of the fields returned are of type Decimal, and as the automatically generated WCF proxy gets XML back it fails with a CommunicationException saying:
"Error in deserializing body of reply message for operation 'getOpenReceivables'."
"There is an error in XML document (1, 941)."
"Input string was not in a correct format."
[Edit] Here is a full stacktrace:
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
at System.Decimal.Parse(String s, NumberStyles style, IFormatProvider provider)
at System.Xml.XmlConvert.ToDecimal(String s)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderExecutePortType.Read2_XXNG_OPEN_RECEIVABLES(Boolean isNullable, Boolean checkType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderExecutePortType.Read3_Item()
at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer.Deserialize(XmlSerializationReader reader)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
I can see that the decimal number returned is formatted with a punctuation as the decimal. For testing purposes I tried a Decimal.Parse("123.99") and got the same error. By setting a
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
before my testcode for Decimal.Parse("123.99") I get that working.
However, setting the CurrentCulture before calling the WCFService method doesn't make any difference.
Are there any ways I can get my WCFService proxy object to understand that the returned XML is in another cultureformat?
回答1:
Have you tried using a custom IClientMessageFormatter.DeserializeReply() implementation? WCF is stuffed to the gills with extensibility points, so it's often hard to know which one to pick, but DeserializeReply looks like the right tool for this job.
Take a look at this blog post-- sorry its original site is offline so pointing to Internet Archive version which looks like a similar thing to what you're trying to do: using a WCF client to call a non-WCF web service, and to do specialized processing of the response to account for non-WCF-like behavior on the server.
Here's a code excerpt from that blog:
public object DeserializeReply(Message message, object[] parameters)
{
object helperInstance = Activator.CreateInstance(_return);
//we have special condition where service sets Http response code to 403 that signals that an error has occured
KeyValuePair<string,object> serviceErrorProperty = message.Properties.FirstOrDefault(p => p.Key == ResponseErrorKey);
if (serviceErrorProperty.Key != null)
{
//we have an error message
IResponseErrorProvider responseErrorProvider = helperInstance as IResponseErrorProvider;
if (responseErrorProvider != null)
{
//unpack the error payload from message and assign to the object
ResponseError payload = message.GetBody<ResponseError>();
responseErrorProvider.ServiceError = payload;
//return fixed null type with error attached to it
return helperInstance;
}
}
//another message we might get is <nil-classes type="array"/> for empty arrays.
XmlDictionaryReader xdr = message.GetReaderAtBodyContents();
xdr.MoveToContent();
if (xdr.Name == NullMessage)
{
return helperInstance; //standin for the null value
}
return _formatter.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
return _formatter.SerializeRequest(messageVersion, parameters);
}
回答2:
You could attach a culture initializer to a behaviour which you then add to an endpoint. This would set the culture on the thread deserializing the response, the field should therefore, be deserialized correctly.
For a code example of this, have a look at this link:
http://blogs.msdn.com/drnick/archive/2008/02/26/using-call-context-initializers-for-culture.aspx
回答3:
I would be very surprised if the culture is really the problem. XML spec says that decimals are using the dot as separator, and the (de)serializer knows this. Can you trace the message and see what's in at position 941? (By the way: I like SoapUI for such things, besides WCF tracing) When it comes to decimals, maybe the different precision might be a problem: maybe the Java service puts more 666666666666 before the trailing 7 in case of two thirds that the .net decimal can handle?
回答4:
And there is a second option: the decimal is not really a decimal (again: tracing the message would be very enlighting). I found this link which laments a little bit about the amazon services, that might put the unit into the decimal value (by the way: WTF!?). However, there is a nice description on how to interact with the raw message before passing it to the deserialization using the IClientMessageInspector interface. If such a strange case is your problem you could either force the service provider to comply with his own contract or do such a trick (if the provider is outside your sphere of control and says "this is by design").
来源:https://stackoverflow.com/questions/1460063/change-culture-when-deserializing-wcf-service