随笔-124  评论-194  文章-0  trackbacks-0
SOAP中不支持HashMap,但可以通过适配器将数组转换成HashMap的方式来支持。

这是通过JAXB2.0中一个适配器类来转换的,先看下这个类的说明:

 

javax.xml.bind.annotation.adapters
类 XmlAdapter<ValueType,BoundType>

java.lang.Object
  继承者 javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>

类型参数:
BoundType - JAXB 不知道如何处理的一些类型。编写一个适配器,以便允许通过 ValueType 将此类型用作内存表示形式。
ValueType - JAXB 无需其他操作便知道如何处理的类型。


这样,我们先定义一个用来传送数据的通用数组,包含了KEY和VALUE两个成员用来存MAP的项:
public class OtherValues {
    
public OtherValues () {};
    
    
public OtherValues (String key, String value) {
        
this.key = key;
        
this.value = value;
    }
;
    
    
public String key; 
    
    
public String value;
}


再定义一个转换类:(数组到HashMap的转换)
import java.util.HashMap;
import java.util.Map.Entry;
import org.apache.log4j.Logger;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class OtherValuesAdapter extends XmlAdapter<OtherValues[], HashMap<String,String>> {
    
static Logger logger = Logger.getLogger (OtherValuesAdapter.class.getName());
    
  
public HashMap<String, String> unmarshal(OtherValues[] value ) {
      logger.error(
"unmarshal begin");
    HashMap
<String, String> r = new HashMap<String,String>();
    
for( OtherValues c : value )
      r.put(c.key, c.value);
    
return r;
  }

  
  
public OtherValues[] marshal( HashMap<String,String> value ) {
      logger.error(
"marshal begin");
      OtherValues[] pairs 
= new OtherValues[value.size ()];
    
int i = 0;
    
for(Entry<String,String> entry : value.entrySet()) {
        pairs[i
++= new OtherValues (entry.getKey(), entry.getValue());
    }

    
return pairs;
  }

}


我们需要在一个结构中来包含使用HashMap的变量,因为必须为这个变量再声明一个@XmlJavaTypeAdapter,这样JAXB才会在收到相应消息时调用我们的转换类。这是结构定义:
import java.util.HashMap;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

public class MapValue {
    
private String devName;
    
private String devIp;

    @XmlJavaTypeAdapter(OtherValuesAdapter.
class)
    
public HashMap <String, String> otherValues;
    
    
public String getDevIp() {
        
return devIp;
    }

    
public void setDevIp(String devIp) {
        
this.devIp = devIp;
    }

    
public String getDevName() {
        
return devName;
    }

    
public void setDevName(String devName) {
        
this.devName = devName;
    }


}


最后,在SOAP服务的声明中,使用这个结构:(注意是sendAlarmMap方法)
import java.util.List;

import javax.jws.*;
import javax.jws.soap.*;
import javax.jws.soap.SOAPBinding.*;

import java.util.HashMap;
import java.util.Map;

@WebService
public interface NotifyService {
    
public int sendAlarm (DeviceValue alarm);
    
    
public String sendAlarmString (String stralarm);
    
    
public List<DeviceValue> sendAlarmArr (List<DeviceValue> arr);

    
public int sendAlarmMap (MapValue m);
    
}



下面,我们来看如何通过JAVA及PERL的方式调用这个服务:
JAVA的方式:
         NotifyService s = (NotifyService) getBean ("notifyClient");

             MapValue mv 
= new MapValue ();
            mv.otherValues 
= new HashMap<String, String> ();
            mv.otherValues.put (
"hehe2""a");
            mv.otherValues.put (
"2""b");

            mv.setDevIp (
"he");
            mv.setDevName (
"hehe2");
            
            
int r = s.sendAlarmMap(mv);
            logger.info(
"recv: " + r);


PERL的方式:
{# call send map alarm
    my @params = (SOAP::Data->name(arg0=>{
        devName
=>"hehe", 
        devIp
=>"ip1",
        otherValues
=>[{
            item 
=> [
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe1", value=>"ip1"}, 
               {key
=>"hehe2", value=>"ip2"}]
        }]
    }));
    
    
my $method = SOAP::Data->name('sendAlarmMap');
    
    
my $result = $soap->call($method => @params);
    
    
print "\nsend map alarm result:\n";
    
if ($result->fault)
    {
       
print $result->faultstring;
    }
    
else
    {
       
print $result->result;
    }
    
print "\n\n";
}


产生的SOAP消息如下:
请求:
<?xml version="1.0" encoding="UTF-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><sendAlarmMap><arg0><devName>hehe</devName><otherValues><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip1</value><key>hehe1</key></item><item><value>ip2</value><key>hehe2</key></item></otherValues><devIp>ip1</devIp></arg0></sendAlarmMap></soap:Body></soap:Envelope>


回应:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns1:sendAlarmMapResponse xmlns:ns1="http://magic.nms.exchangebit.com/"><return>99</return></ns1:sendAlarmMapResponse></soap:Body></soap:Envelope>


总结:
有了转换器这个工具,我们可以在SOAP的JAXB绑定里支持各种JAVA的COLLECTION类型,以及自定义类型,打破了SOAP原始支持类型的限制。
posted on 2007-10-29 16:41 我爱佳娃 阅读(7207) 评论(5)  编辑  收藏 所属分类: web技术

评论:
# re: 在CXF中用JAXB数据绑定支持HashMap类型 2007-10-29 23:38 | dihin
如果返回类型是一个Map类型的话 该怎么做?
上次项目就遇到这个问题  回复  更多评论
  
# re: 在CXF中用JAXB数据绑定支持HashMap类型 2007-10-30 10:14 | 我爱佳娃
我做了实际实验,返回值里用上面的方法也是可以的呀:
结构声明和转换类不变,增加服务方法:
public MapValue sendAlarmRetMap () {
MapValue mv = new MapValue ();
mv.otherValues = new HashMap<String, String> ();
mv.otherValues.put ("hehe2", "a");
mv.otherValues.put ("2", "b");

mv.setDevIp ("he");
mv.setDevName ("hehe2");

logger.info("return map");

return mv;
}

在测试代码中,增加调用:
{
MapValue mv = s.sendAlarmRetMap();
logger.info("recv: " + mv.getDevIp() + mv.otherValues.get ("hehe2"));
//this.assertEquals(r.size(), 10);
}

可以看到转换类同样是起作用的。我估计你没有把MAP放到一个结构中,只有这样声明的转换类才能起作用。  回复  更多评论
  
# re: 在CXF中用JAXB数据绑定支持HashMap类型 2007-10-30 16:56 | dihin
我把Map改为
public class MyMap {
private HashMap<String,String> map;
//getter/setter...
}
没用转换类也可以直接调用
wsdl类型为
- <xs:complexType name="myMap">
- <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:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
  回复  更多评论
  
# re: 在CXF中用JAXB数据绑定支持HashMap类型 2007-10-30 20:53 | 我爱佳娃
@dihin
我开始就是这样的,什么也不做,WSDL文件生成可以,但到实际调用时不能成功。还原不了MAP。
你是不是用的Aegis数据绑定?似乎这种可以直接支持,JAXB只能加XMLADAPTER。  回复  更多评论
  
# re: 在CXF中用JAXB数据绑定支持HashMap类型 2012-07-18 18:44 | jerry.lees
这样转换以后 传中文会有乱码问题,该怎么解决啊?  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: