WCF: реализация IClientMessageFormatter SerializeRequest для управления пространствами имен xml - PullRequest
3 голосов
/ 04 августа 2011

Мой проект на C # .NET должен общаться с внешним веб-сервисом на платформе Java (Sonic ESB).Для разработки и тестирования у меня есть фиктивный сервис, работающий в SOAP UI.Веб-сервис совместно использует объект с именем «ShipmentInformationMessage», который мой код должен создать и заполнить данными, а затем передать их веб-сервису.

Когда я через некоторое время заставил его работать вместе, я заметил, что сообщения с запросами, зарегистрированные в SOAP UI, имели следующий формат:

 <?xml version="1.0" encoding="UTF-8"?>
 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
     <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
         <ShipmentInformationMessage 
               xmlns="http://www.noneofyour.biz/message/Transportation/2011/01">
             <SenderId>NOSOTROS</SenderId>
             <RecipientId>PARTNER</RecipientId>
             <CreationTimeStamp>2011-08-03T11:53:36.6505521+02:00</CreationTimeStamp>
             <Version>2.0</Version>
             <TestIndicator>true</TestIndicator>
             <ControlParty>
                 <Name xmlns="http://www.noneofyour.biz/schema/Common/2011/01">PrimaryContact</Name>
                 <Contact xsi:nil="true" xmlns="http://www.noneofyour.biz/schema/Common/2011/01"/>
             </ControlParty>
             <Action>new</Action>
             <Shipments>
                 <Shipment>
                     <MasterSystemId xmlns="http://www.noneofyour.biz/schema/Transportation/2011/01">FargoGateInbound</MasterSystemId>
                     <OwnerId xmlns="http://www.noneofyour.biz/schema/Transportation/2011/01">DKPARCELS</OwnerId>
                     <TrackingCode xmlns="http://www.noneofyour.biz/schema/Transportation/2011/01">ConsignmentNo</TrackingCode>
                     <DatesAndTimes xmlns="http://www.noneofyour.biz/schema/Transportation/2011/01">
                         <ShipmentDateTime>2011-01-23T12:34:00</ShipmentDateTime>
                     </DatesAndTimes>
 etcetera...           

и так далее ...

Как видите, пространства имен xml добавляются к нескольким узлам, а не объявляются вверху, а затем добавляются к именам элементов с префиксами.Это вызовет проблемы в реальном веб-сервисе, с которым ему придется работать (не спрашивайте меня почему).

Вместо этого нам хотелось бы получить следующее:

 <?xml version="1.0" encoding="UTF-8"?>
 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       <ShipmentInformationMessage 
            xmlns:ns1="http://www.noneofyour.biz/message/Transportation/2011/01" 
            xmlns:ns2="http://www.noneofyour.biz/schema/Transportation/2011/01"  
            xmlns:ns3="http://www.noneofyour.biz/schema/Common/2011/01">
         <ns1:SenderId>NOSOTROS</ns1:SenderId>
         <ns1:RecipientId>PARTNER</ns1:RecipientId>
         <ns1:CreationTimeStamp>2011-07-01T13:31:14.7164012+02:00</ns1:CreationTimeStamp>
         <ns1:Version>2.0</ns1:Version>
         <ns1:TestIndicator>true</ns1:TestIndicator>
         <ns1:ControlParty>
           <ns3:Name>PrimaryContact</ns3:Name>
           <ns3:Contact d6p1:nil="true" />
         </ns1:ControlParty>
         <ns1:Action>new</ns1:Action>
         <ns1:Shipments>
           <ns1:Shipment>
             <ns2:MasterSystemId>FargoGateInbound</ns2:MasterSystemId>
             <ns2:OwnerId>DKPARCELS</ns2:OwnerId>
             <ns2:TrackingCode>ConsignmentNo</ns2:TrackingCode>
             <ns2:DatesAndTimes>
               <ns2:ShipmentDateTime>2011-01-23T12:34:00</ns2:ShipmentDateTime>
             </ns2:DatesAndTimes>
       etcetera...           

и так далее ...

После некоторых исследований я приступил к разработке своего пользовательского средства форматирования запросов, расширив IClientMessageFormatter,а затем подключить его, добавив его к поведению операций.По крайней мере, это прошло хорошо.Тем не менее, я не был уверен, как реализовать метод SerializeRequest, и не смог найти никаких полезных примеров в интернете, поэтому немного побрел вперед и в итоге получил следующее:

public class SonicMessageFormatter : IClientMessageFormatter
{
    private IClientMessageFormatter _InnerFormatter;

    public SonicMessageFormatter(IClientMessageFormatter innerFormatter)
    {
        _InnerFormatter = innerFormatter;
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        PutShipmentInformationMessage operation = (PutShipmentInformationMessage)parameters[0];
        ShipmentInformationMessage sim = operation.ShipmentInformationMessage;

        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("ns1", "http://www.noneofyour.biz/message/Transportation/2011/01");
        ns.Add("ns2", "http://www.noneofyour.biz/schema/Transportation/2011/01");
        ns.Add("ns3", "http://www.noneofyour.biz/schema/Common/2011/01");

        XmlSerializer xs = new XmlSerializer(sim.GetType());

        MemoryStream ms = new MemoryStream();
        StreamWriter writer = new StreamWriter(ms, Encoding.UTF8);
        xs.Serialize(writer, sim);
        Message requestMessage = Message.CreateMessage(messageVersion, sim.Action.ToString(), writer);
        writer.Flush();

        return requestMessage;
    }

    public object DeserializeReply(Message message, object[] parameters)
    {
        return _InnerFormatter.DeserializeReply(message, parameters);
    }
}

При тестировании с этимЯ получил следующую ошибку:

 System.Runtime.Serialization.SerializationException: Type 'System.Text.UTF8Encoding+UTF8Encoder' with data contract name 'UTF8Encoding.UTF8Encoder:http://schemas.datacontract.org/2004/07/System.Text' is not expected. Add any types not known statically to the list of known types

Поэтому я изменил код, добавив (изменив следующие строки:

        Type[] knownTypes = new Type[1];
        knownTypes[0] = Encoding.UTF8.GetEncoder().GetType();
        XmlSerializer xs = new XmlSerializer(sim.GetType(), knownTypes);

Но теперь я получаю следующую ошибку:

System.InvalidOperationException: System.Text.UTF8Encoding.UTF8Encoder cannot be serialized because it does not have a parameterless constructor.

Ну, черт! Что мне теперь делать ??


РЕДАКТИРОВАТЬ Я добавляю wsdl службы mock для помощи в решении следующих проблем:

 <wsdl:definitions xmlns:ns="http://www.noneofyour.biz/message/Transportation/2011/01" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:auto1="http://www.noneofyour.biz/message/Transportation/2011/01" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://www.noneofyour.biz/message/Transportation/2011/01">
    <wsdl:types>
       <xsd:schema>
          <xsd:import namespace="http://www.noneofyour.biz/message/Transportation/2011/01" schemaLocation="/mockShipmentInformationService_SOAPBinding?WSDL&interface=ShipmentInformationService_SOAPBinding&part=ShipmentInformationMessage.xsd"/>
       </xsd:schema>
    </wsdl:types>
    <wsdl:message name="ShipmentInformationMessage">
       <wsdl:part name="ShipmentInformationMessage" element="ns:ShipmentInformationMessage"></wsdl:part>
    </wsdl:message>
    <wsdl:portType name="ShipmentInformationService">
       <wsdl:operation name="PutShipmentInformationMessage">
          <wsdl:input message="ns:ShipmentInformationMessage"></wsdl:input>
       </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="ShipmentInformationService_SOAPBinding" type="ns:ShipmentInformationService">
       <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
       <wsdl:operation name="PutShipmentInformationMessage">
          <soap:operation soapAction="http://www.noneofyour.biz/ShipmentInformationService/PutShipmentInformationMessage" style="document"/>
          <wsdl:input>
             <soap:body use="literal"/>
          </wsdl:input>
       </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="ShipmentInformationService_Service">
       <wsdl:port name="ShipmentInformationServicePort" binding="ns:ShipmentInformationService_SOAPBinding">
          <soap:address location="http://localhost:8088/mockShipmentInformationService_SOAPBinding"/>
       </wsdl:port>
    </wsdl:service>
 </wsdl:definitions>

РЕДАКТИРОВАТЬ Здесь следуют верхние части для сообщений, генерируемых форматером по умолчанию и пользовательским форматером, соответственно:

Default ClientMessageFormatter (InnerFormatter), который работает:

 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Header>
     <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.noneofyour.biz/ShipmentInformationService/PutShipmentInformationMessage</Action>
   </s:Header>
   <s:Body 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <ShipmentInformationMessage 
         xmlns="http://www.noneofyour.biz/message/Transportation/2011/01">
       <SenderId>NOSOTROS</SenderId>
       <RecipientId>PARTNER</RecipientId>
       <CreationTimeStamp>2011-08-05T10:42:38.9344907+02:00</CreationTimeStamp>
       <Version>2.0</Version>
       <TestIndicator>true</TestIndicator>
       <ControlParty>
         <Name xmlns="http://www.noneofyour.biz/schema/Common/2011/01">PrimaryContact</Name>
         <Contact xsi:nil="true" xmlns="http://www.noneofyour.biz/schema/Common/2011/01" />
       </ControlParty>
       <Action>new</Action>
etcetera...

Пользовательский ClientMessageFormatter (SonicMessageFormatter), который не работает:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.noneofyour.biz/ShipmentInformationService/PutShipmentInformationMessage</Action>
  </s:Header>
  <s:Body>
    <ShipmentInformationMessage 
         xmlns:ns1="http://www.noneofyour.biz/message/Transportation/2011/01" 
         xmlns:ns2="http://www.noneofyour.biz/schema/Transportation/2011/01" 
         xmlns:ns3="http://www.noneofyour.biz/schema/Common/2011/01">
      <ns1:SenderId>NOSOTROS</ns1:SenderId>
      <ns1:RecipientId>PARTNER</ns1:RecipientId>
      <ns1:CreationTimeStamp>2011-08-05T13:45:36.9134685+02:00</ns1:CreationTimeStamp>
      <ns1:Version>2.0</ns1:Version>
      <ns1:TestIndicator>true</ns1:TestIndicator>
      <ns1:ControlParty>
        <ns3:Name>PrimaryContact</ns3:Name>
        <ns3:Contact d6p1:nil="true">
        </ns3:Contact>
      </ns1:ControlParty>
      <ns1:Action>new</ns1:Action>
 etcetera...

Как видите, сообщение пользовательского форматера не имеет пространств имен, объявленных в узле Body в отличие отсообщение форматера по умолчанию.Я также пытался без добавления пространств имен в сериализатор, но это тоже не помогло.

Ответы [ 2 ]

1 голос
/ 10 августа 2011

Я наконец получил его на работу !!! Проблема заключалась в том, что ShipmentInformationMessage не получил префикс и, следовательно, не был распознан фиктивной службой (очевидно). После МНОГО поиска в Интернете я наконец-то попробовал код из этого превосходного блога MSDN Эндрю Арнотта !

Я изменил код, чтобы можно было добавить пространства имен xml, подключил его, и теперь он работает !!!

Вот так выглядит верх рабочего сообщения:

 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Header>
     <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.noneofyour.biz/ShipmentInformationService/PutShipmentInformationMessage</Action>
   </s:Header>
   <s:Body>
     <ns1:ShipmentInformationMessage xmlns:ns2="http://www.noneofyour.biz/schema/Transportation/2011/01" xmlns:ns3="http://www.noneofyour.biz/schema/Common/2011/01" xmlns:ns1="http://www.noneofyour.biz/message/Transportation/2011/01">
       <ns1:SenderId>NOSOTROS</ns1:SenderId>
       <ns1:RecipientId>PARTNER</ns1:RecipientId>
       <ns1:CreationTimeStamp>2011-08-10T07:44:52.9423961+02:00</ns1:CreationTimeStamp>
       <ns1:Version>2.0</ns1:Version>
       <ns1:TestIndicator>true</ns1:TestIndicator>
       <ns1:ControlParty>
         <ns3:Name>PrimaryContact</ns3:Name>
         <ns3:Contact d5p1:nil="true" xmlns:d5p1="http://www.w3.org/2001/XMLSchema-instance" />
       </ns1:ControlParty>
       <ns1:Action>new</ns1:Action>

Обратите внимание на префикс элемента ShipmentInformationMessage, который добивается цели!

Теперь я понимаю, что для обеспечения альтернативы форматирующему устройству по умолчанию, основанному на DataContractSerializer WCF, простой XmlSerialzer не обрезает его. Вам нужно что-то такое же хорошее, как DataContractSerializer.


EDIT

Вот фрагмент кода, который делает трюк здесь:

            XmlRootAttribute root = new XmlRootAttribute(wrapperName);
            root.Namespace = wrapperNamespace;
            serializer = new XmlSerializer(this.objectType, root);

Здесь определяется корневой элемент, добавляется необходимое пространство имен и инициализируется xml-сериализатор. Я передаю "ShipmentInformationMessage" как wrapperName, а также его пространство имен.

Затем, со всеми тремя пространствами имен и их префиксами, добавленными впоследствии к xml-сериализатору (как в примере кода моего исходного поста), я получаю именно то, что мне нужно, и служба имитации SOAP UI полностью принимает это!

1 голос
/ 05 августа 2011

Используемая вами перегрузка Message.CreateMessage принимает MessageVersion, действие (строку) и объект , представляющий тело сообщения. WCF пытается сериализовать экземпляр StreamWriter, и это определенно не то, что вам нужно.

Вы можете использовать другую перегрузку, которая принимает XmlReader, которая содержит информацию о теле. Тот должен делать то, что тебе нужно:

    MemoryStream ms = new MemoryStream();
    XmlWriterSettings writerSettings = new XmlWriterSettings
    {
        Encoding = Encoding.UTF8,
        OmitXmlDeclaration = true
    };
    XmlWriter writer = new XmlWriter.Create(ms, writerSettings);
    xs.Serialize(writer, sim, ns);  
    writer.Flush();
    ms.Position = 0;
    XmlReader reader = XmlReader.Create(ms);
    Message requestMessage = Message.CreateMessage(messageVersion, sim.Action.ToString(), reader);

    return requestMessage;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...