2013-04-18 63 views
4

我試圖通過SOAP傳遞Hashmap。我正在使用CXF wsdl2java創建我的模式。而且我爲我的HashMap創建了一個包裝類,因爲Hashmap本身無法通過該行傳遞。使用JAXB通過SOAP傳遞HashMap <String,Object>

然後,我創建了適配器,將該Hashmap變形爲我的wsdl的已知類型,但是當我創建了wsdl時,它添加了一些不需要的抽象映射。下面是代碼:

這裏是我的HashMap的

@XmlRootElement(name = "testTO") 
public class TestTO { 

    private HashMap<String, Object> mapTest; 

    public TestTO(){ 
     this.mapTest = new HashMap<String, Object>(); 
    } 

    @XmlJavaTypeAdapter(MapAdapter.class) 
    public HashMap<String, Object> getMapTest() { 
     return mapTest; 
    } 

    public void setMapTest(HashMap<String, Object> mapTest) { 
     this.mapTest = mapTest; 
    } 

} 

這裏包裝類是MyMap中類中,是一個已知的模式類型

@XmlJavaTypeAdapter(MapAdapter.class) 
public class MyMap extends HashMap<String, Object>{ 
    public final List<Entry> entryList = new ArrayList<Entry>(); 
} 

這是入門級中該列表包含以上內容:

public class Entry { 

    @XmlAttribute 
    public String key; 

    @XmlElements({ 
      @XmlElement(name = "byte", type = byte.class), 
      @XmlElement(name = "short", type = short.class), 
      @XmlElement(name = "int", type = int.class), 
      @XmlElement(name = "long", type = long.class), 
      @XmlElement(name = "float", type = float.class), 
      @XmlElement(name = "double", type = double.class), 
      @XmlElement(name = "char", type = char.class), 
      @XmlElement(name = "boolean", type = boolean.class), 

      @XmlElement(name = "ByteWrapper", type = Byte.class), 
      @XmlElement(name = "ShortWrapper", type = Short.class), 
      @XmlElement(name = "IntegerWrapper", type = Integer.class), 
      @XmlElement(name = "LongWrapper", type = Long.class), 
      @XmlElement(name = "FloatWrapper", type = Float.class), 
      @XmlElement(name = "DoubleWrapper", type = Double.class), 
      @XmlElement(name = "Character", type = Character.class), 
      @XmlElement(name = "BooleanWrapper", type = Boolean.class), 

      @XmlElement(name = "BigDecimal", type = BigDecimal.class), 
      @XmlElement(name = "String", type = String.class), 
      @XmlElement(name = "Date", type = Date.class) 
    }) 
    public Object value; 

    public Entry() { 
     this.key = null; 
     this.value = null; 
    } 

    public Entry(String key, Object value) { 
     this.key = key; 
     this.value = value; 
    } 

    public String getKey() { 
     return key; 
    } 

    public Object getValue() { 
     return value; 
    } 

} 

這是我的適配器:

public class MapAdapter extends XmlAdapter<MyMap, Map<String, Object>> { 

    @Override 
    public MyMap marshal(Map<String, Object> v) throws Exception { 
     MyMap myMap = new MyMap(); 

     for (Map.Entry<String, Object> e : v.entrySet()) { 
      Entry entry = new Entry(); 
      entry.key = e.getKey(); 
      entry.value = e.getValue(); 

      myMap.entryList.add(entry); 
     } 
     return myMap; 
    } 

    @Override 
    public Map<String, Object> unmarshal(MyMap v) throws Exception { 
     Map<String, Object> map = new HashMap<String,Object>(); 
      for (Entry e : v.entryList) { 
       map.put(e.key, e.value); 
      } 
    return map; 
    } 

} 

但我的WSDL是產生如下:

<xs:element minOccurs="0" name="foo" type="tns:testTO"/> 
</xs:sequence> 
</xs:complexType> 
<xs:complexType name="testTO"> 
<xs:sequence> 
<xs:element minOccurs="0" name="mapTest" type="tns:myMap"/> 
</xs:sequence> 
</xs:complexType> 
<xs:complexType name="myMap"> 
<xs:complexContent> 
<xs:extension base="tns:hashMap"> 
<xs:sequence> 
<xs:element maxOccurs="unbounded" minOccurs="0" name="entryList" nillable="true" type="tns:entry"/> 
</xs:sequence> 
</xs:extension> 
</xs:complexContent> 
</xs:complexType> 
<xs:complexType name="hashMap"> 
<xs:complexContent> 
<xs:extension base="tns:abstractMap"> 
<xs:sequence/> 
</xs:extension> 
</xs:complexContent> 
</xs:complexType> 
<xs:complexType abstract="true" name="abstractMap"> 
<xs:sequence/> 
</xs:complexType> 
<xs:complexType name="entry"> 
<xs:sequence> 
<xs:choice minOccurs="0"> 
<xs:element name="byte" type="xs:byte"/> 
<xs:element name="short" type="xs:short"/> 
<xs:element name="int" type="xs:int"/> 
<xs:element name="long" type="xs:long"/> 
<xs:element name="float" type="xs:float"/> 
<xs:element name="double" type="xs:double"/> 
<xs:element name="char" type="xs:unsignedShort"/> 
<xs:element name="boolean" type="xs:boolean"/> 
<xs:element name="ByteWrapper" type="xs:byte"/> 
<xs:element name="ShortWrapper" type="xs:short"/> 
<xs:element name="IntegerWrapper" type="xs:int"/> 
<xs:element name="LongWrapper" type="xs:long"/> 
<xs:element name="FloatWrapper" type="xs:float"/> 
<xs:element name="DoubleWrapper" type="xs:double"/> 
<xs:element name="Character" type="xs:unsignedShort"/> 
<xs:element name="BooleanWrapper" type="xs:boolean"/> 
<xs:element name="BigDecimal" type="xs:decimal"/> 
<xs:element name="String" type="xs:string"/> 
<xs:element name="Date" type="xs:dateTime"/> 
</xs:choice> 
</xs:sequence> 
<xs:attribute name="key" type="xs:string"/> 
</xs:complexType> 

我已經看過了,我已經在這裏發現沒有人能解決我的問題,我甚至引用http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html 但多個其他案件java的wsdl似乎在搞亂架構。

謝謝。

回答

2

我想出的解決方案與我所尋找的解決方案類似於polbotinka提到的解決方案,但我添加了日期的附加綁定和適配器。 TestTO類被我的界面上的所有對象擴展,以便爲每個對象以地圖格式傳遞靈活的屬性。下面是我做的:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name="TestTO", namespace="test/common") 
public abstract class TestTO { 

    @XmlJavaTypeAdapter(MapAdapter.class) 
    private Map<String, Object> elements; 
} 

這個類,然後產生類似下面的(這是我的CXF-的Java2WSDL插件所產生的整體WSDL的一部分)的模式:

<xs:complexType abstract="true" name="testTO"> 
<xs:sequence> 
    <xs:element name="elements"> 
    <xs:complexType> 
     <xs:sequence> 
     <xs:element maxOccurs="unbounded" minOccurs="0" name="entry"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element minOccurs="0" name="key" type="xs:string"/> 
       <xs:element minOccurs="0" name="value" type="xs:anyType"/> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 
</xs:sequence> 

<jaxws:bindings wsdlLocation="wsdl/TestImpl.wsdl" 
     xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" 
     xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
     jaxb:version="2.1"> 
    <jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='http://namespace.goes.here']"> 
     <jaxb:bindings node="//xs:complexType[@name='testTO']//xs:element[@name='elements']"> 
      <jaxb:property> 
       <jaxb:baseType name="java.util.Map&lt;String,Object&gt;" /> 
      </jaxb:property> 
     </jaxb:bindings> 
     <jaxb:serializable/> 
    </jaxws:bindings> 
</jaxws:bindings> 

的生成的版本:然後我生成過程中使用下面的結合文件從CXF-WSDL2JAVA的TestTO如下所示:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "testTO", propOrder = { 
    "elements" 
}) 
public abstract class TestTO { 

    @XmlElement(required = true, type = TestTO.Elements.class) 
    protected Map<String, Object> elements; 

    public Map<String, Object> getElements() { 
     return elements; 
    } 

    public void setElements(Map<String, Object> value) { 
     this.elements = value; 
    } 

    @XmlAccessorType(XmlAccessType.FIELD) 
    @XmlType(name = "", propOrder = { 
     "entry" 
    }) 
    public static class Elements { 

     protected List<TestTO.Elements.Entry> entry; 

     public List<TestTO.Elements.Entry> getEntry() { 
      if (entry == null) { 
       entry = new ArrayList<TestTO.Elements.Entry>(); 
      } 
      return this.entry; 
     } 

     @XmlAccessorType(XmlAccessType.FIELD) 
     @XmlType(name = "", propOrder = { 
      "key", 
      "value" 
     }) 
     public static class Entry { 

      protected java.lang.String key; 
      protected java.lang.Object value; 

      public java.lang.String getKey() { 
       return key; 
      } 

      public void setKey(java.lang.String value) { 
       this.key = value; 
      } 

      public java.lang.Object getValue() { 
       return value; 
      } 

      public void setValue(java.lang.Object value) { 
       this.value = value; 
      } 
     } 
    } 
} 

所以現在生成的類有一個地圖與被自動轉換爲內部列表類接口。所以我想創建一個適配器來將一些數據類型轉換成我想要使用的類型。日期是這種情況下的特定情況,因爲我通常使用JAXB綁定文件從模式進行日期轉換,但由於模式是「anyType」,因此綁定文件無法工作。因此,使用上面的MapAdapter.class將地圖中輸入對象上的XmlGregorianCalendars轉換爲日期。

public class MapAdapter extends XmlAdapter<TestTO.Elements, Map<String, Object>>{ 
    @Override 
    public Map<String, Object> unmarshal(TestTO.Elements v) throws Exception { 
     Map<String, Object> map = new HashMap<String, Object>(); 

     if(v != null && v.entry != null){ 
      for(Entry e : v.entry){ 
       if(e.getValue() instanceof XMLGregorianCalendar) 
        map.put(e.getKey(), ((XMLGregorianCalendar)e.getValue()).toGregorianCalendar().getTime()); 
       else 
        map.put(e.getKey(), e.getValue()); 
      } 
     } 
     return map; 
    } 

    @Override 
    public TestTO.Elements marshal(Map<String, Object> v) throws Exception { 
     TestTO.Elements b = new TestTO.Elements(); 
     if(v == null) 
      return null; 
     for(java.util.Map.Entry<String, Object> e : v.entrySet()){ 
      Entry newEntry = new Entry(); 
      newEntry.setKey(e.getKey()); 
      newEntry.setValue(e.getValue()); 
      b.getEntry().add(newEntry); 
     } 
     return b; 
    } 
} 

對於這個適配器的工作,我不得不有一個版本的「生成的類」來模仿內部Elements類。所以我有一個common.adapter.TestTO.class,它是生成的一個common.normal.TestTO.class,它是我的所有其他類在我的界面上擴展的類。

這裏是插件的配置我在客戶端使用的一代:

<plugin> 
       <groupId>org.apache.cxf</groupId> 
       <artifactId>cxf-codegen-plugin</artifactId> 
       <version>${cxf.version}</version> 
       <executions> 
        <execution> 
         <id>generate-sources</id> 
         <phase>generate-sources</phase> 
         <configuration> 

          <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot> 
          <wsdlOptions> 
           <wsdlOption> 
            <wsdl>${basedir}/src/main/resources/META-INF/wsdl/TestImpl.wsdl</wsdl> 
            <bindingFiles> 
             <bindingFile>${basedir}/src/main/resources/META-INF/binding.xml</bindingFile> 
            </bindingFiles> 
           </wsdlOption> 
          </wsdlOptions> 
         </configuration> 
         <goals> 
          <goal>wsdl2java</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 
4

我相信您不必將自定義XmlAdapter寫入到具有最新JAXB版本的marshall/unmarshall Map<String, Object。下面的示例適合我。

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "foo") 
public class Foo { 
    private Map<String, Object> map = new HashMap<String, Object>(); 

    public Map<String, Object> getMap() { 
    return params; 
    } 
} 

這導致了一個模式:

<xs:complexType name="foo"> 
    <xs:sequence> 
    <xs:element name="map"> 
     <xs:complexType> 
     <xs:sequence> 
      <xs:element maxOccurs="unbounded" minOccurs="0" name="entry"> 
      <xs:complexType> 
       <xs:sequence> 
       <xs:element minOccurs="0" name="key" type="xs:string"/> 
       <xs:element minOccurs="0" name="value" type="xs:anyType"/> 
       </xs:sequence> 
      </xs:complexType> 
      </xs:element> 
     </xs:sequence> 
     </xs:complexType> 
    </xs:element> 
    </xs:sequence> 
</xs:complexType> 

那麼你應該能夠來解讀以下XML:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://your.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
    <soapenv:Header/> 
    <soapenv:Body> 
     <foo> 
     <params> 
      <entry> 
       <key>string</key> 
       <value xsi:type="xs:string">5</value> 
      </entry> 
      <entry> 
       <key>integer</key> 
       <value xsi:type="xs:int">54</value> 
      </entry> 
     </params> 
     </foo> 
    </soapenv:Body> 
</soapenv:Envelope> 

只是不要忘了XS和XSI的命名空間。您甚至可以將您的自定義類型作爲值傳遞給不僅僅是簡單的xsi類型。然後你必須確保你已經指定了正確的xsi:type.

+0

是的,這工作對我來說很好,但在但問題是,從該模式生成的類,它不容許我設置的實際的hashmap作爲輸入對象。哪些仍在發生 – bhcmoney

+0

第一個代碼段中存在一個錯誤。 '地圖'必須是'params',反之亦然。 – yurin

相關問題