java 使用 JAXB 通过 SOAP 传递 HashMap<String,Object>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16086385/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pass a HashMap<String,Object> over SOAP using JAXB
提问by bhcmoney
I am trying to pass a Hashmap over SOAP. I am using CXF wsdl2java to create my schema. And I have created a wrapper class for my HashMap since Hashmap itself cannot be passed over the line.
我正在尝试通过 SOAP 传递 Hashmap。我正在使用 CXF wsdl2java 创建我的架构。我已经为我的 HashMap 创建了一个包装类,因为 Hashmap 本身不能通过线路传递。
I have then created adapters to morph that Hashmap into a known type for my wsdl but when my wsdl is created it adds some unneeded abstract map. Below is the code:
然后我创建了适配器来将该 Hashmap 变形为我的 wsdl 的已知类型,但是当我的 wsdl 创建时,它添加了一些不需要的抽象映射。下面是代码:
Here is my wrapper class for the HashMap
这是我的 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;
}
}
Here is the MyMap class in which is a known schema type
这是 MyMap 类,其中是已知的模式类型
@XmlJavaTypeAdapter(MapAdapter.class)
public class MyMap extends HashMap<String, Object>{
public final List<Entry> entryList = new ArrayList<Entry>();
}
This is the Entry Class in which that list contains above:
这是该列表包含上面的条目类:
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;
}
}
This is my adapter:
这是我的适配器:
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;
}
}
But my wsdl is generating the following:
但是我的 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>
I have looked at multiple other cases that I have found here and none of them were able to solve my problem I have even referenced http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.htmlbut the wsdl to java seems to be messing the schema up.
我查看了我在这里找到的多个其他案例,但没有一个能够解决我的问题我什至引用了http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/ annotation/adapters/XmlAdapter.html但 wsdl 到 java 似乎搞乱了模式。
Thanks.
谢谢。
采纳答案by bhcmoney
The solution I came up with that worked for what I was looking for was similar to what polbotinka mentioned but I added the additional bindings and adapters for dates. The TestTO class is extended by all of the objects on my interface as a way to have flexible attributes passed in a map format per object. Here is what I did:
我想出的解决方案适用于我正在寻找的内容与 polbotinka 提到的类似,但我为日期添加了额外的绑定和适配器。TestTO 类由我的界面上的所有对象扩展,作为一种方式,使每个对象以映射格式传递灵活的属性。这是我所做的:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="TestTO", namespace="test/common")
public abstract class TestTO {
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String, Object> elements;
}
This class would then generate a schema like the following (which was part of my overall WSDL generated by CXF-Java2Wsdl plugin):
然后这个类将生成一个如下所示的模式(这是我由 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>
I then used the following binding file during generation:
然后我在生成过程中使用了以下绑定文件:
<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<String,Object>" />
</jaxb:property>
</jaxb:bindings>
<jaxb:serializable/>
</jaxws:bindings>
</jaxws:bindings>
The generated version of the TestTO from CXF-Wsdl2Java looks as follows:
从 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;
}
}
}
}
So now the generated class has a Map to interface with that is automatically converted to the inner list class. So I then wanted to create an adapter to convert some of the datatypes into the types I wanted to use. Date was the specific one in this case as I usually used a JAXB binding file to do the dates conversion from the schema but since the schema was "anyType" that binding file would not work. So that MapAdapter.class from above was used to convert XmlGregorianCalendars on the input object within the map into Dates.
所以现在生成的类有一个映射到接口,它会自动转换为内部列表类。所以我想创建一个适配器来将一些数据类型转换成我想要使用的类型。在这种情况下,日期是特定的,因为我通常使用 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;
}
}
For this adapter to work I had to have a version of the "generated class" to mimic the inner Elements class. So I had a common.adapter.TestTO.class which was the generated one and a common.normal.TestTO.class which was the one that all the rest of my classes extended on my interface.
为了让这个适配器工作,我必须有一个“生成的类”的版本来模仿内部的 Elements 类。所以我有一个 common.adapter.TestTO.class 是生成的,还有一个 common.normal.TestTO.class 是我所有其他类在我的界面上扩展的。
Here is the Plugin configuration I used in the client generation:
这是我在客户端生成中使用的插件配置:
<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>
回答by polbotinka
I believe you don't have to write the custom XmlAdapter
to marshall/unmarshall Map<String, Object
with the newest JAXB versions. The below sample works fine for me.
我相信您不必使用最新的 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;
}
}
This is resulted in a schema:
这导致了一个模式:
<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>
Then you should be able to unmarshall the following xml:
然后您应该能够解组以下 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>
Just don't forget about xs and xsi namespaces. You can even pass your custom types as values not just simple xsi types. Then you have to make sure you have specified the proper xsi:type.
只是不要忘记 xs 和 xsi 命名空间。您甚至可以将自定义类型作为值传递,而不仅仅是简单的 xsi 类型。然后你必须确保你指定了正确的xsi:type.