1
  1. SOAPWebサービス用のWSDLを持っています

  2. RAD Developer(IBM Websphereで使用されるEclipseベースのコンパイラ)で「トップダウンのJava Bean」Webサービスクライアントを作成し、一連のJAX-WS.javaモジュールを自動生成しました。

  3. 操作の1つに対して自動生成されたJAX-WSコードは次のとおりです。


@WebMethod(operationName = "CommitTransaction", action = "http://myuri.com/wsdl/gitsearchservice/CommitTransaction")

@RequestWrapper(localName = "CommitTransaction", targetNamespace = "http://myuri.com/wsdl/gitsearchservice", className = "com.myuri.shwsclients.CommitTransaction")
@ResponseWrapper(localName = "CommitTransactionResponse", targetNamespace = "http://myuri.com/wsdl/gitsearchservice", className = "com.myuri.shwsclients.CommitTransactionResponse")
public void commitTransaction(
    @WebParam(name = "requestOptions", targetNamespace = "http://myuri.com/wsdl/gitsearchservice")
    RequestOptions requestOptions,
    @WebParam(name = "transactionData", targetNamespace = "http://myuri.com/wsdl/gitsearchservice")
    TransactionData transactionData);

質問:

  • 「transactionData」は、大きくて複雑なXMLデータレコードから取得されます。WSDL形式は、Java側で作成するXMLと完全に一致し、Webサービスがサーバー側で読み取るものと完全に一致します。

  • Q:「transactionData」パラメーターのJavaシリアル化をバイパスして、SOAPメッセージで生のXMLを送信するにはどうすればよいですか?XMLを読み取って解析し、Javaの「TransactionType」構造をフィールドごとにパックする必要はありませんか?

前もって感謝します!

4

2 に答える 2

4

私は2つのアプローチを見ます:

  1. 複雑で、生成されたコードが再生成されるとすぐに崩壊します...生成されたサービスプロキシクラスで作成されたService、Dispatch、BindingProviderの実装を掘り下げます。独自のBindingProvider実装ですが、そこに到達するには他の置換を行う必要があります。

  2. XMLシリアル化を実行しますが、「フィールドごとにパックする」という手間はかかりません。

期待される形式に「完全に一致する」と言う生のXMLの文字列から始めます

String rawXML = someMethodThatReturnsXml();
JAXBContext context = JAXBContext.newInstance(TransactionData.class);
Unmarshaller unmarshaller =  context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(new StringReader(rawXML));
TransactionData data = (TransactionData) obj;

jaxbで生成されたクラス「TransactionData」クラスに「XmlRootElement」という注釈が付けられていない場合でも、次のようにこれを実行できるはずです。

String rawXML = someMethodThatReturnsXml();
JAXBContext context = JAXBContext.newInstance(TransactionData.class);
Unmarshaller unmarshaller =  context.createUnmarshaller();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource input = new InputSource(new StringReader(rawXML));
Document doc = db.parse(input);
JAXBElement<?> element = unmarshaller.unmarshal(doc, TransactionData.class);
Object obj = element.getValue();

TransactionData data = (TransactionData) obj;

さまざまなタイプの多数のXMLレコードを処理する場合は、これらをまとめて、目的の出力クラスをパラメーターにし、汎用のxml-to-objectユーティリティを使用できます。

import java.io.IOException;
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
 * @author <a href="http://stackoverflow.com/users/1904450/zachofalltrades">Zach Shelton</a>
 */
public class SampleCode {
    /**
     * Turn xml into an object. 
     *
     * @param <SomeJaxbType>
     * @param wellFormedXml a String of well-formed XML, with proper reference to the correct namespace
     * @param jaxbClass the class representing the type of object you want to get back (probably a class generated from xsd or wsdl) 
     * @return an object of the requested type
     * @throws JAXBException if there is a problem setting up the Unmarshaller, or performing the unmarshal operation
     * @throws SAXException if the given class is not annotated as XmlRootElement, and the xml String can not be parsed to a generic DOM Document
     */
    public <SomeJaxbType> SomeJaxbType xmlToObject(String wellFormedXml, Class<SomeJaxbType> jaxbClass) throws JAXBException, SAXException {
        if (jaxbClass==null) throw new IllegalArgumentException("received null jaxbClass");
        if (wellFormedXml==null || wellFormedXml.trim().isEmpty()) throw new IllegalArgumentException("received null or empty xml");
        if (!jaxbClass.isAnnotationPresent(XmlType.class)) throw new IllegalArgumentException(jaxbClass.getName() + " is not annotated as a JAXB class");

        JAXBContext context = JAXBContext.newInstance(jaxbClass);
        Unmarshaller unmarshaller =  context.createUnmarshaller();

        Object genericObject;
        if (jaxbClass.isAnnotationPresent(XmlRootElement.class)) {
            genericObject = unmarshaller.unmarshal(new StringReader(wellFormedXml));
        } else {//must use alternate method described in API
                //http://docs.oracle.com/javaee/6/api/javax/xml/bind/Unmarshaller.html#unmarshalByDeclaredType
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db;
            try {
                db = dbf.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                throw new IllegalStateException("failed to get DocumentBuilder from factory");
            }
            InputSource input = new InputSource(new StringReader(wellFormedXml));
            Document doc;
            try {
                doc = db.parse(input);
            } catch (IOException e) {
                throw new IllegalStateException("xml string vanished");
            }
            JAXBElement<?> element = unmarshaller.unmarshal(doc, jaxbClass);
            genericObject = element.getValue();
        }

        SomeJaxbType typedObject = (SomeJaxbType) genericObject;
        return typedObject;
    }
}
于 2013-01-23T17:58:48.347 に答える
1

RequestWrapperとアノテーションの使用法についてはあまり詳しくありませんResponseWrapperが、送信メッセージは次のようになりますか?

<CommitTransaction>
    <requestOptions>...</requestOptions>
    <transactionData>...</transactionData>
</CommitTransaction>

そうだとしましょう:)そして、TransactionDataparamのXMLがインスタンスによって表されていると仮定しSourceます。SOAPHandlerそれへのハンドルを維持するカスタムを作成しますSource

public class TransactionDataHandler implements SOAPHandler<SOAPMessageContext> {
    private final Source transactionDataSource;

    public TransactionDataHandler(Source transactionDataSource) {
        this.transactionDataSource = transactionDataSource;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        // no exception handling
        Boolean isOutbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (Boolean.TRUE.equals(isOutbound)) {
            SOAPMessage message = context.getMessage();
            SOAPBody body = message.getSOAPBody();
            Node commitTransactionNode = body.getFirstChild();
            Result commitTransactionResult = new DOMResult(commitTransactionNode);
            TransformerFactory.newInstance().newTransformer().transform(this.transactionDataSource, commitTransactionResult);
        }
        return true;
    }

    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return true;
    }

    @Override
    public void close(MessageContext context) {
        // no-op
    }
}

<transactionData>変換ステップで子ノードを作成する必要があるという考え方です。HandlerResolverまた、おそらく次のようなカスタムも必要になります。

public class TransactionDataHandlerResolver implements HandlerResolver {
    private final Handler transactionDataHandler;

    public TransactionDataHandlerResolver(Source transactionDataSource) {
        this.transactionDataHandler = new TransactionDataHandler(transactionDataSource);
    }

    @Override
    public List<Handler> getHandlerChain(PortInfo portInfo) {
        return Collections.singletonList(this.transactionDataHandler);
    }
}

最後に、Serviceインスタンスを作成し、XMLにフックしSourceHandlerResolver

Source transactionDataSource;
URL wsdlDocumentLocation;
QName serviceName;
Service service = Service.create(wsdlDocumentLocation, serviceName);
service.setHandlerResolver(new TransactionDataHandlerResolver(transactionDataSource));

ここから、Dispatchまたはポートプロキシを取得して、操作を開始できます。これは既存のコード/環境に正確に適合しない可能性がありますが、うまくいけば、それはあなたに思考の糧を与えるでしょう...

編集:ポートプロキシを使用している場合はnull、2番目の引数を渡します。

port.commitTransaction(requestOptions, null);
于 2013-01-23T19:33:24.923 に答える