Изменить культуру при десериализации службы WCF

Я использую С# для чтения данных из 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."

[Изменить] Вот полная трассировка стека:

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 находится в другом культурном формате?


person Frode Lillerud    schedule 22.09.2009    source источник


Ответы (4)


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

Взгляните на этот пост в блоге -- извините, его исходный сайт недоступен, поэтому он указывает на версию Internet Archive, что похоже на то, что вы пытаетесь сделать: используя Клиент 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);
}
person Justin Grant    schedule 26.09.2009
comment
На самом деле я решил проблему, отредактировав файл .xsd, предоставленный мне парнем, который создал веб-сервис. (Получил файл .wsdl и два .xsd). Вы получили баллы, так как это кажется хорошим решением и хорошей ссылкой, но я на самом деле не пробовал. - person Frode Lillerud; 01.10.2009
comment
Вы должны были по крайней мере вставить наиболее подходящий код из статьи в блоге - ссылка мертва - person Camilo Terevinto; 07.03.2017
comment
извините @CamiloTerevinto - это было почти 7 лет назад, когда я написал это, задолго до того, как была формализована передовая практика обязательного вставки кода вместе со ссылками. Я нашел блог в Интернет-архиве и обновил свой ответ ссылкой на архив, а также фрагментом кода. - person Justin Grant; 08.03.2017

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

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

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

person Shiraz Bhaiji    schedule 30.09.2009

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

person Marc Wittke    schedule 28.09.2009

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

person Marc Wittke    schedule 28.09.2009