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 ですが、Java への wsdl がスキーマを台無しにしているようです。

ありがとう。

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;
            }
        }
    }
}

そのため、生成されたクラスには、内部リスト クラスに自動的に変換されるインターフェイスへのマップが含まれています。そのため、一部のデータ型を使用したい型に変換するアダプターを作成したいと考えました。この場合、日付は特定のものでした。通常は JAXB バインディング ファイルを使用してスキーマから日付を変換しましたが、スキーマが「anyType」であるため、そのバインディング ファイルは機能しませんでした。上記の MapAdapter.class を使用して、マップ内の入力オブジェクトの XmlGregorianCalendars を Dates に変換しました。

 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 に答える