5

我正在尝试通过 SOAP 传递 Hashmap。我正在使用 CXF wsdl2java 创建我的模式。我已经为我的 HashMap 创建了一个包装类,因为 Hashmap 本身不能通过线路传递。

然后,我创建了适配器以将该 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 但 wsdl 到 java 似乎正在搞乱架构。

谢谢。

4

2 回答 2

5

我相信您不必使用最新的 JAXB 版本编写自定义XmlAdapter编组/解组。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.

于 2015-05-06T07:10:07.893 回答
3

我想出的适用于我正在寻找的解决方案与 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;
            }
        }
    }
}

所以现在生成的类有一个 Map 接口,它会自动转换为内部列表类。所以我想创建一个适配器来将一些数据类型转换成我想要使用的类型。在这种情况下,日期是特定的,因为我通常使用 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>
于 2015-06-24T14:28:18.960 回答