Принять простой формат null для параметра XmlElement - PullRequest
1 голос
/ 09 февраля 2012

Я строю службу WCF (.Net 3.5, IIS Hosted) для замены старой службы в стиле ASMX. Он должен быть очень совместимым с интерфейсом старого стиля, чтобы избежать усилий со стороны продавцов, которые пишут программное обеспечение, которое его вызывает. (Некоторые из них создают простую структуру данных XML, разбивают ее внутри заранее подготовленного «шаблона» SOAP и бросают в мою службу. Мне нужно принять их существующую структуру XML).

Для совместимости с тем, как эти клиенты вызывают службу, мне пришлось определить операцию как:

[ServiceContract(Namespace = "urn:namespacex")]
public interface IServices
{
    [OperationContract]
    System.Xml.XmlElement OperationA(int parmB, System.Xml.XmlElement parmC);
}

т.е. параметры находятся в OperationContract, а не извлекаются в отдельные элементы DataMember в DataContract. Используемый здесь XmlElement заменяет параметр XmlNode, использовавшийся в старой службе ASMX.

Он реализован как:

[ServiceBehavior(Namespace = "urn:namespacey")]
public class TheService : IServices
{
    public System.Xml.XmlElement OperationA(int parmB, System.Xml.XmlElement parmC)
    {
        ... code to handle call
    }
}

Работает нормально ... когда есть данные для отправки.

Проблема, с которой я сталкиваюсь, заключается в том, что вход parmC равен нулю, что разрешено Иногда у него есть данные, иногда нет. Один вызывающий абонент отправляет это в сообщении SOAP для нулевого parmC:

<parmC/>

т.е. простой пустой элемент XML.

Это вызывает следующую ошибку из WCF:

Ожидается состояние «Элемент». Обнаружен «EndElement» с именем 'parmC', пространство имен 'urn: namespacex'.

Так что, похоже, не нравится простой ввод нулевого элемента. (Служба работает нормально, если там есть какие-то данные.)

Отслеживая мой собственный вызывающий тест (который работает с и без информации в параметре parmC), я вижу, что для нуля мой (.Net WCF) тестер отправляет:

<parmC xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:nil="true"/>

Попытка 1:

Обращаясь за помощью, я обратил внимание на атрибут «[XmlSerializerFormat]», который должен сделать сервис более похожим на старый стиль ASMX, не используя более новый сериализатор контракта данных. Мой вызывающий тест (.Net, WCF) тогда вообще ничего не отправляет для нуля. Но этот клиент по-прежнему получает ошибку, хотя теперь это другое сообщение в отношении этого элемента:

Соответствующий начальный элемент не открыт.

Попытка 2:

Потом я вспомнил, что самая старая версия оригинального сервиса ASMX использовала строку для принятия этих данных. (Он загрузил эту строку в объектный объект XML на стороне сервера в обработчике операции). Поэтому я изменил контракт и операцию, чтобы определить parmC как строку вместо XmlElement.

Внезапно служба принимает различные формы пустых parmC. (Используя приложение для быстрого тестирования, которое отправляет сырой SOAP в мой сервис, чтобы я мог смоделировать, что делают эти поставщики).

Но - если там действительно есть данные, они терпят неудачу, с:

Ошибка в десериализации тела сообщения запроса на операцию 'OperatonA. ---> System.InvalidOperationException: есть ошибка в документе XML (99, 99). ---> System.Xml.XmlException: конечный элемент ожидается 'parmC' из пространства имен 'urn: namespacex'. Найден элемент «а» из пространства имен ''

(когда данные XML отправляются в parmC, в SOAP это выглядит так:

<parmC>
  <a xmlns="">bbb</a>
</parmC>

)

Ясно, что в этом случае он не ожидает найти XML внутри строки (элемент 'a'); но раньше это работало в ASMX, поэтому стоило того.


Таким образом, я не могу найти комбинацию настроек, которая позволяет отправлять в службу XML-данные, а также разрешать этим клиентам стиль пустых или нулевых данных.

Должен ли я реализовать собственный десериализатор? Есть ли где-нибудь настройка, которая поможет?

(Пока мне удалось сохранить все, что связано с WCF, чисто в конфигурации, я бы хотел сохранить это так, если смогу.)

Таким образом, мне нужен сервис WCF, который справится с любым из этих событий по сети:

<s:Body>
  <OperationA xmlns="urn:namespacex">
    <parmB>1</parmB>
    <parmC> { this works fine }
     <a xmlns="">
      <b>bbb</b>
     </a>
    </parmC>
  </OperationA>
</s:Body>

<s:Body>
  <OperationA xmlns="urn:namespacex">
    <parmB>1</parmB>
    <parmC/> { I need this to be accepted }
  </OperationA>
</s:Body>

1 Ответ

1 голос
/ 10 февраля 2012

XML для двух представленных вами вариантов не эквивалентен.Существует очень тонкое различие между ними, что, вероятно, является причиной вашей проблемы.Опция <a xmlns=""> устанавливает для элемента a и его дочерних элементов отсутствие по умолчанию пространства имен XML .Другая опция (опция параметра «null») не содержит «переопределения» пространства имен XML по умолчанию от «urn: namespacex» до «», поэтому, когда процесс десериализации анализирует parmC для элемента <a xmlns="">, он можетне найти это.Если вы внимательно прочитали сообщение об исключении, оно фактически говорит вам об этом.

Что касается поддержки двух сценариев, попробуйте заставить своего клиента отправить это вместо того, что они отправляют сейчас:

<parmC>
 <a xmlns="">
  <b />
 </a>
</parmC>

или, возможно:

<parmC>
 <a xmlns="" />
</parmC>
...