Как научить JAXB, используемый клиентом Apache CXF JAX-WS, демаршировать {http://microsoft.com/wsdl/types/}guid значений в нетипизированном свойстве? - PullRequest
0 голосов
/ 21 декабря 2018

У меня есть служба SOAP, к которой нужно отправлять запросы (в частности, Веб-служба интеграции Ivanti ).

Я использую Apache CXF 3.2.7 для подключения к службе.Я генерирую Java-классы из WSDL службы, используя wsdl2java.

. WSDL не упоминает никаких GUID и кажется полностью самодостаточным.Однако есть одно поле (с именем Value), которое не является типизированным, т.е.е.xsd:element без атрибута type, и сервер отправляет ответы со значениями различных типов в этом поле.Они выглядят так:

Строки и шорты в порядке,но GUID выдают это исключение на клиенте:

javax.xml.bind.UnmarshalException: unrecognized type name: {http://microsoft.com/wsdl/types/}guid

Как мне избежать этого исключения? На самом деле меня не волнует значение этого поля, хотя решение, которое достигаетБезошибочное типизированное шифрование, конечно, было бы идеально.

То, что я пробовал

Независимо от того, что я делаю, исключение просто не исчезает.В частности, я попытался:

  • добавить <jaxb:binding><jaxb:property><jaxb:baseType> в мой настраиваемый XML-код привязки, чтобы он обрабатывал поле как строку - это делало свойство Java строкой, но, по-видимому, не нарушало маршалингданные в соответствии с указанными типами и разбиты, потому что они не могут преобразовать дату в строку;

  • добавление <jaxb:javaType> или <jxc:javaType> с помощью пользовательского метода демаршаллинга - это неработать вообще, wsdl2java не удалось с «компилятором не удалось выполнить эту настройку преобразования.Он присоединен в неправильном месте или несовместим с другими привязками », независимо от того, где я разместил элемент и какой тип Java я указал;

  • добавление определения типа вручную из один из этих источников :

    <xs:schema elementFormDefault="qualified" targetNamespace="http://microsoft.com/wsdl/types/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:simpleType name="guid">
        <xs:restriction base="xs:string">
          <xs:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:schema>
    

    в файл службы WSDL перед вызовом wsdl2java файла - после добавления xsd:elementв дополнение к xsd:simpleType я наконец получил wsdl2java для генерации метода на ObjectFactory с аннотацией @XmlElementDecl(namespace = "http://microsoft.com/wsdl/types/", name = "guid"), но этот метод все еще не использовался, простой String все еще использовался везде, где мой WSDL ссылался на guid, и UnmarshalException сохраняется;

  • , даже добавляя in-Interceptor на фазе USER_STREAM, которая съедает весь InputStream в строку, жестоко находитвсе вещи, которые выглядят как атрибуты GUID xsi:type / xmlns:q2 и заменяют их на xsi:type="xsd:string" аналогично этому ответу - но я, должно быть, допустил некоторую ошибку, потому что исключение все еще не ушел;Вот мой код:

    import java.io.ByteArrayInputStream;
    import java.io.InputStream;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    
    public class GuidExpungeInterceptor extends AbstractPhaseInterceptor<Message> {
      private static class GuidExpungedInputStream extends ByteArrayInputStream {
        private final InputStream stream;
    
        public GuidExpungedInputStream(InputStream stream) throws IOException {
          super(guidExpungedByteArray(stream));
          this.stream = stream;
        }
    
        private static byte[] guidExpungedByteArray(InputStream stream) throws IOException {
          String content = IOUtils.toString(stream, StandardCharsets.ISO_8859_1);
          content = content.replaceAll("<Value xsi:type=\"([A-Za-z_][A-Za-z0-9_.-]*):guid\" xmlns:\\1=\"http://microsoft.com/wsdl/types/\">", "<Value xsi:type=\"xsd:string\">");
          return content.getBytes(StandardCharsets.ISO_8859_1);
        }
    
        @Override
        public void close() throws IOException {
          stream.close();
          super.close();
        }
      }
    
      public GuidExpungeInterceptor() {
        super(Phase.USER_STREAM);
      }
    
      @Override
      public void handleMessage(Message message) {
        if (message == message.getExchange().getInMessage()) {
          try {
            InputStream stream = message.getContent(InputStream.class);
            message.setContent(InputStream.class, new GuidExpungedInputStream(stream));
          } catch (IOException e) {
            throw new Fault(e);
          }
        }
      }
    }
    
    class BlahController {
      BlahController() {
        JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
        proxyFactory.setServiceClass(FRSHEATIntegrationSoap.class);
        proxyFactory.setAddress(this.properties.getFrsHeatIntegrationUrl());
        this.service = (FRSHEATIntegrationSoap) proxyFactory.create();
    
        Client client = ClientProxy.getClient(service);
        client.getInInterceptors().add(new GuidExpungeInterceptor());
      }
    }
    

    Затем я использую this.service для вызова строго типизированных методов работы.Возможно, перехватчик не сохраняется за пределами локальной переменной client?

Если я правильно понимаю (в чем я совсем не уверен), это исключение означает, что JAXB не 'У меня нет демаршаллера, зарегистрированного для типа GUID, и это должно быть решено, если бы я мог каким-то образом завладеть реестром JAXB и добавить свой собственный маршаллер.Но, посмотрев на JavaDocs в CXF, я не представляю, как или даже могу ли я получить доступ к этому реестру.Некоторые методы звучат так, будто я могу получить JAXBContext, но я не понимаю, как я мог бы добавить что-либо к уже существующему экземпляру JAXBContext.

1 Ответ

0 голосов
/ 26 декабря 2018

Если вы импортируете источники, сгенерированные wsdl2java из вашего исходного WSDL, в ваш источник управления (и прекратите генерировать их при каждой сборке), вы можете добавить собственный класс, отображающий simpleType:

import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;

@XmlType(namespace = "http://microsoft.com/wsdl/types/", name = "guid")
public class Guid {
   @XmlValue
   public String guid;
}

и добавьте аннотацию @XmlSeeAlso(Guid.class) к одному из ваших wsdl2java сгенерированных классов, которые уже подобраны JAXB, e.г.фактический класс обслуживания.Класс обслуживания, вероятно, уже имеет @XmlSeeAlso({ObjectFactory.class}), так что вы можете просто изменить его на @XmlSeeAlso({ObjectFactory.class, Guid.class}).

Таким образом, JAXB успешно разархивирует GUID как Guid экземпляры с простым содержимым строки.Если вам нужны фактические java.util.UUID экземпляры, вы можете добавить @XmlJavaTypeAdapter в поле @XmlValue, но я не проверял это.

(Кстати: когда вы пытались добавить xsd:element в WSDL, я думаю, вы добавили отображение для XML-элемента с именем guid, например <q2:guid xmlns:q2="http://microsoft.com/wsdl/types/">c3aca40a-439d-4af2-b42e-59b1ddcf3d6e</q2:guid>. Это не то, что вы хотели, поэтому это объясняет, почему это не помогло вам.)

...