WebServiceのMapオブジェクトの処理方法
11455 ワード
最近、WebServiceについて議論しました.現在Smartでは、SOAPサービスとRESTサービスの2種類のWebServiceを公開しています.必要に応じて自由に選択できます.
今日、皆さんと共有したいのは、WebServiceで複雑なJavaデータ型を処理するソリューションです.
一般的なJavaデータ型、JavaBean、ListではSOAPサービスが完全に処理(シーケンス化と逆シーケンス化)できますが、これらは問題ありませんが、Mapオブジェクトにとっては少し面倒なようです.
次の例を見てください.
Productを作成するには、Mapタイプのパラメータを渡す必要があります.このインタフェースを実装するのは難しくないはずですが、クライアントがMapオブジェクトを渡すことができるかどうかが鍵です.
Whateverクライアントを使って検証します
クライアントを開発するのも難しくないようですが、肝心なのはSOAPHelperを使って、ProductServiceのエージェントオブジェクトを作成してくれたので、次のすべてはそんなに簡単です.
実行して、結果がどうなっているか見てみましょう.
異常は私たちに言った:Marshalling Error:java.util.これはjavaですutil.Mapシーケンス化エラー.
SOAP果はMapオブジェクトを処理できないようですね.どうやって解決しますか?
SOAPにとって、確かに少し複雑で、JDKのJAXB規範は私たちに解決策を提供してくれた.
XmlAdapter(XMLアダプタ)をカスタマイズし、MapオブジェクトをSOAPで処理できるオブジェクトに変換する必要があります.
私たちがやったことは2つあります. StringObjectMapAdapterクラス拡張javaxを定義する.xml.bind.annotation.adapters.XmlAdapterは、Mapオブジェクトを変換することを目的としています. javaxを使用する.xml.bind.annotation.adapters.XmlJavaType Adapter注記、変換するMapオブジェクトを表示します.
Come on!
StringObjectMapAdapterクラスを書き、XmlAdapterクラスを継承します.2つの方法を実装するだけでいいです. unmarshal:逆シーケンス化し、DataオブジェクトをMapオブジェクトに変換します. marshal:シーケンス化し、MapオブジェクトをDataオブジェクトに変換します.
なお、ここでのDataはStringObjectMapAdapterの静的内部クラスとしてもよく、もちろん独立して存在してもよい.Dataクラスにはもう一つの静的内部クラスEntryがあります.これは実際にはMapのいくつかのエントリであり、MapをリストでEntryのパッケージと見なすことができます.これは私たちが見たDataクラスです.
次に、メソッドのパラメータに@XmlJavaType Adapter注記を1つ使用するだけで、StringObjectMapAdapterをMapに適用する必要があります.
これでWebServiceを再度呼び出すと、実行に成功した情報が表示されます!
このソリューションで複雑なのはStringObjectMapAdapterです.そして、Mapタイプのデータを処理できるだけで、他の異なる汎用的なMapオブジェクトには何もできません.他の対応するXxxMapAdapterを書くしかありません.確かに振り回されています.
RESTにとって、以上は何でもないようですが、信じられないなら下を見てください.
まずRESTサービスを書きます.
インタフェースは免除して、直接サービスクラスのためにRESTサービスを発表して、私达は入力と出力のデータ型を定义することができて、すべてJSONでしょう、もちろんXMLです.
RESTクライアントをもう1つ書きます.
ここでは、SOAPHelperではなく、RESTHelperがRESTクライアントエージェントオブジェクトを取得するために使用されます.さらにWSDLではなくWADLを使用しています.
運転して、完全に正しい!
オブジェクトのシーケンス化において,RESTは確かにSOAPより優れているようである.実際にシーンを適用してSOAPしか使用できない場合は、できるだけMapオブジェクトを回避すべきで、本当にだめならXmlAdapterのソリューションしか使用できません.条件が許すなら、できるだけRESTを使うことをお勧めします.
一部の友人が疑問を提起しているかもしれませんが、Securityの面では、RESTもSOAPのようなWS-securityソリューションを提供していますか?将来また皆さんとこの方面の問題を討論する機会があるでしょう.
今日、皆さんと共有したいのは、WebServiceで複雑なJavaデータ型を処理するソリューションです.
一般的なJavaデータ型、JavaBean、ListではSOAPサービスが完全に処理(シーケンス化と逆シーケンス化)できますが、これらは問題ありませんが、Mapオブジェクトにとっては少し面倒なようです.
次の例を見てください.
@WebService(value = "/soap/ProductService", type = WebService.Type.SOAP)
public interface ProductService {
boolean createProduct(Map<String, Object> productFieldMap);
}
Productを作成するには、Map
Whateverクライアントを使って検証します
public class ProductServiceSOAPTest {
private String wsdl = "http://localhost:8080/smart-sample/ws/soap/ProductService";
private ProductService productService = SOAPHelper.createClient(wsdl, ProductService.class);
@Test
public void createProductTest() {
Map<String, Object> productFieldMap = new HashMap<String, Object>();
productFieldMap.put("productTypeId", 1);
productFieldMap.put("name", "1");
productFieldMap.put("code", "1");
productFieldMap.put("price", 1);
productFieldMap.put("description", "1");
boolean result = productService.createProduct(productFieldMap);
Assert.assertTrue(result);
}
}
クライアントを開発するのも難しくないようですが、肝心なのはSOAPHelperを使って、ProductServiceのエージェントオブジェクトを作成してくれたので、次のすべてはそんなに簡単です.
実行して、結果がどうなっているか見てみましょう.
org.apache.cxf.interceptor.Fault: Marshalling Error: java.util.Map is not known to this context
at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:265)
at org.apache.cxf.jaxb.io.DataWriterImpl.write(DataWriterImpl.java:169)
at org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor.writeParts(AbstractOutDatabindingInterceptor.java:114)
at org.apache.cxf.interceptor.BareOutInterceptor.handleMessage(BareOutInterceptor.java:68)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:565)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:474)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:377)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:330)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
at org.apache.cxf.frontend.ClientProxy.invoke(ClientProxy.java:81)
at com.sun.proxy.$Proxy31.createProduct(Unknown Source)
at com.smart.sample.test.ProductServiceSOAPTest.createProductTest(ProductServiceSOAPTest.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: javax.xml.bind.MarshalException
- with linked exception:
[javax.xml.bind.JAXBException: java.util.Map is not known to this context]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:326)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:251)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.writeObject(JAXBEncoderDecoder.java:612)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:240)
... 38 more
Caused by: javax.xml.bind.JAXBException: java.util.Map is not known to this context
at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:247)
at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:262)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:148)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:131)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:333)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:76)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
... 42 more
Caused by: javax.xml.bind.JAXBException: java.util.Map is not known to this context
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:624)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:145)
... 48 more
...
異常は私たちに言った:Marshalling Error:java.util.これはjavaですutil.Mapシーケンス化エラー.
SOAP果はMapオブジェクトを処理できないようですね.どうやって解決しますか?
SOAPにとって、確かに少し複雑で、JDKのJAXB規範は私たちに解決策を提供してくれた.
XmlAdapter(XMLアダプタ)をカスタマイズし、MapオブジェクトをSOAPで処理できるオブジェクトに変換する必要があります.
私たちがやったことは2つあります.
Come on!
public class StringObjectMapAdapter extends XmlAdapter<StringObjectMapAdapter.Data, Map<String, Object>> {
@Override
public Map<String, Object> unmarshal(Data data) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
for (Data.Entry entry : data.getList()) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
@Override
public Data marshal(Map<String, Object> map) throws Exception {
Data data = new Data();
for (Map.Entry<String, Object> entry : map.entrySet()) {
data.addEntry(entry.getKey(), entry.getValue());
}
return data;
}
public static class Data {
private List<Entry> list = new ArrayList<Entry>();
public void addEntry(String fieldName, Object fieldValue) {
Entry entry = new Entry();
entry.setKey(fieldName);
entry.setValue(fieldValue);
list.add(entry);
}
public List<Entry> getList() {
return list;
}
public void setList(List<Entry> list) {
this.list = list;
}
public static class Entry {
private String key;
private Object value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
}
}
StringObjectMapAdapterクラスを書き、XmlAdapterクラスを継承します.2つの方法を実装するだけでいいです.
なお、ここでのDataはStringObjectMapAdapterの静的内部クラスとしてもよく、もちろん独立して存在してもよい.Dataクラスにはもう一つの静的内部クラスEntryがあります.これは実際にはMapのいくつかのエントリであり、MapをリストでEntryのパッケージと見なすことができます.これは私たちが見たDataクラスです.
次に、メソッドのパラメータに@XmlJavaType Adapter注記を1つ使用するだけで、StringObjectMapAdapterをMap
@WebService(value = "/soap/ProductService", type = WebService.Type.SOAP)
public interface ProductService {
boolean createProduct(@XmlJavaTypeAdapter(StringObjectMapAdapter.class) Map<String, Object> productFieldMap);
}
これでWebServiceを再度呼び出すと、実行に成功した情報が表示されます!
このソリューションで複雑なのはStringObjectMapAdapterです.そして、Map
RESTにとって、以上は何でもないようですが、信じられないなら下を見てください.
まずRESTサービスを書きます.
@Bean
@WebService(value = "/rest/ProductService", type = WebService.Type.REST)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ProductService extends BaseService {
@POST
@Path("/product")
@Transaction
public boolean createProduct(Map<String, Object> productFieldMap) {
return DataSet.insert(Product.class, productFieldMap);
}
}
インタフェースは免除して、直接サービスクラスのためにRESTサービスを発表して、私达は入力と出力のデータ型を定义することができて、すべてJSONでしょう、もちろんXMLです.
RESTクライアントをもう1つ書きます.
public class ProductServiceRESTTest {
private String wadl = "http://localhost:8080/smart-sample/ws/rest/ProductService";
private ProductService productService = RESTHelper.createClient(wadl, ProductService.class);
@Test
public void createProductTest() {
Map<String, Object> productFieldMap = new HashMap<String, Object>();
productFieldMap.put("productTypeId", 1);
productFieldMap.put("name", "1");
productFieldMap.put("code", "1");
productFieldMap.put("price", 1);
productFieldMap.put("description", "1");
boolean result = productService.createProduct(productFieldMap);
Assert.assertTrue(result);
}
}
ここでは、SOAPHelperではなく、RESTHelperがRESTクライアントエージェントオブジェクトを取得するために使用されます.さらにWSDLではなくWADLを使用しています.
運転して、完全に正しい!
オブジェクトのシーケンス化において,RESTは確かにSOAPより優れているようである.実際にシーンを適用してSOAPしか使用できない場合は、できるだけMapオブジェクトを回避すべきで、本当にだめならXmlAdapterのソリューションしか使用できません.条件が許すなら、できるだけRESTを使うことをお勧めします.
一部の友人が疑問を提起しているかもしれませんが、Securityの面では、RESTもSOAPのようなWS-securityソリューションを提供していますか?将来また皆さんとこの方面の問題を討論する機会があるでしょう.