Изменить культуру при десериализации службы WCF - PullRequest
1 голос
/ 22 сентября 2009

Я использую C # для чтения данных из Java-веб-сервиса.

Я создал ссылку на службу в VS2008 на сервер и могу вызвать один метод, который там есть. Однако некоторые возвращаемые поля имеют тип Decimal, и поскольку автоматически сгенерированный прокси WCF получает XML обратно, происходит сбой с сообщением CommunicationException:

"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] Вот полная трассировка стека:

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)

Я вижу, что возвращаемое десятичное число отформатировано с помощью пунктуации в качестве десятичного числа. В целях тестирования я попробовал Decimal.Parse ("123.99") и получил ту же ошибку. Установив

System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

перед моим тестовым кодом для Decimal.Parse ("123.99") я получаю, что работает.

Однако установка CurrentCulture перед вызовом метода WCFService не имеет никакого значения.

Есть ли какие-либо способы заставить мой прокси-объект WCFService понять, что возвращаемый XML находится в другом формате?

Ответы [ 4 ]

2 голосов
/ 26 сентября 2009

Вы пытались использовать пользовательскую реализацию IClientMessageFormatter.DeserializeReply () ? WCF заполнен жабрами с точки зрения расширяемости, поэтому часто трудно понять, какой из них выбрать, но DeserializeReply выглядит как правильный инструмент для этой работы.

Взгляните на это сообщение в блоге - извините, его оригинальный сайт не в сети, поэтому указывает на версию Интернет-архива , которая похожа на то, что вы пытаетесь сделать: использование клиента WCF вызвать веб-службу, отличную от WCF, и выполнить специализированную обработку ответа на учетную запись для поведения, не похожего на WCF, на сервере.

Вот выдержка из этого блога:

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);
}
1 голос
/ 30 сентября 2009

Вы можете присоединить инициализатор культуры к поведению, которое затем добавляете к конечной точке. Это установит культуру в потоке десериализации ответа, поэтому поле должно быть правильно десериализовано.

Для примера кода, посмотрите на эту ссылку:

http://blogs.msdn.com/drnick/archive/2008/02/26/using-call-context-initializers-for-culture.aspx

0 голосов
/ 28 сентября 2009

И есть второй вариант: десятичная дробь на самом деле не является десятичной (опять же: отслеживание сообщения будет очень полезным). Я нашел эту ссылку , которая немного оплакивает сервисы amazon, которые могут переводить единицу в десятичное значение (кстати: WTF !?). Тем не менее, есть хорошее описание того, как взаимодействовать с необработанным сообщением перед его передачей в десериализацию с использованием интерфейса IClientMessageInspector. Если в таком странном случае у вас возникли проблемы, вы можете либо заставить поставщика услуг выполнить свой собственный контракт, либо сделать такой трюк (если поставщик находится за пределами вашей сферы контроля и говорит «это умышленно»).

0 голосов
/ 28 сентября 2009

Я был бы очень удивлен, если культура действительно проблема. Спецификация XML говорит, что десятичные дроби используют точку в качестве разделителя, и (de) сериализатор знает это. Можете ли вы отследить сообщение и увидеть, что находится в позиции 941? (Между прочим: мне нравится SoapUI для таких вещей, кроме трассировки WCF) Когда речь идет о десятичных дробях, может быть проблема в различной точности: может быть, сервис Java ставит больше 666666666666 перед конечным 7 в случае из двух третей, которые может обработать .net decimal?

...