8/5/11:更新接近帖子底部
我正在尝试使用kSOAP从我的Android应用程序调用我的WCF web服务方法。该方法有4个参数。其中3个是Guid类型(用于编组Java的UUID),最后一个是自定义类型:LocationLibrary.Location。这种类型是在一个单独的DLL (LocationLibrary)中,我加载到WCF web服务项目中,它基本上由两个double组成,纬度和经度。
[OperationContract]
byte[] GetData(Guid deviceId, Guid appId, Guid devKey, LocationLibrary.Location loc);
项目LocationLibrary中的Location类非常简单:
namespace LocationLibrary
{
public class Location
{
public double latitude { get; set; }
public double longitude { get; set; }
public new string ToString()
{
return latitude.ToString() + "," + longitude.ToString();
}
public bool Equals(Location loc)
{
return this.latitude == loc.latitude && this.longitude == loc.longitude;
}
}
}
在我的Android项目中,我创建了一个名为"Location"的类,它类似于。net版本:
public class Location {
public double latitude;
public double longitude;
public Location() {}
public static Location fromString(String s)
{
//Format from SOAP message is "anyType{latitude=39.6572799682617; longitude=-78.9278602600098; }"
Location result = new Location();
String[] tokens = s.split("=");
String lat = tokens[1].split(";")[0];
String lng = tokens[2].split(";")[0];
result.latitude = Double.parseDouble(lat);
result.longitude = Double.parseDouble(lng);
return result;
}
public String toString()
{
return Double.toString(latitude) + "," + Double.toString(longitude);
}
}
当使用kSOAP连接到web服务时,我做了以下操作:
private final String SOAP_ACTION = "http://tempuri.org/IMagicSauceV3/GetData";
private final String OPERATION_NAME = "GetData";
private final String WSDL_TARGET_NAMESPACE = "http://tempuri.org/";
private final String SOAP_ADDRESS = "http://mydomain.com/myservice.svc";
private final SoapSerializationEnvelope envelope;
SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE, OPERATION_NAME);
request.addProperty(Cloud8Connector.deviceIdProperty);
request.addProperty(Cloud8Connector.appIdProperty);
request.addProperty(Cloud8Connector.devKeyProperty);
PropertyInfo locationProperty = new PropertyInfo();
locationProperty.setName("loc");
locationProperty.setValue(Cloud8Connector.myLoc);
locationProperty.setType(Location.class);
request.addProperty(locationProperty);
envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
MarshalUUID mu = new MarshalUUID();
mu.register(envelope);
MarshalLocation ml = new MarshalLocation();
ml.register(envelope);
byte[] result = null;
HttpTransportSE httpRequest = new HttpTransportSE(SOAP_ADDRESS);
try
{
httpRequest.call(SOAP_ACTION, envelope);
String payloadString = ((SoapPrimitive)(envelope.getResponse())).toString();
result = Base64.decode(payloadString, Base64.DEFAULT);
}
catch(Exception e)
{
e.printStackTrace();
}
可以看到,我为Location创建了一个简单的Marshal类,它基本上只使用fromString和toString方法。我将信封注册到marshal实例的Location:
MarshalLocation ml = new MarshalLocation();
ml.register(envelope);
Location的marshal类:
public class MarshalLocation implements Marshal
{
public Object readInstance(XmlPullParser parser, String namespace, String name,
PropertyInfo expected) throws IOException, XmlPullParserException {
return Location.fromString(parser.nextText());
}
public void register(SoapSerializationEnvelope cm) {
cm.addMapping(cm.xsd, "Location", Location.class, this);
}
public void writeInstance(XmlSerializer writer, Object obj) throws IOException {
writer.text(((Location)obj).toString());
}
}
然而,我得到这个错误从WCF返回,似乎不能让它工作。从错误和搜索,我想我需要调整一些与我的web服务,但我不确定究竟是什么是最好的方法来解决这个问题。
我试过把这个添加到web上。web服务配置文件:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="Location, LocationLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
<knownType type="Location, LocationLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"></knownType>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
我已经尝试将ServiceKnownType属性添加到接口方法签名:
[OperationContract]
[ServiceKnownType(typeof(LocationLibrary.Location))]
byte[] GetData(Guid deviceId, Guid appId, Guid devKey, LocationLibrary.Location loc);
但是我仍然得到这个错误:(
你能给我指个正确的方向吗?谢谢!错误:07-30 00:42:08.186: WARN/System.err(8723): SoapFault - faultcode:'a:DeserializationFailed' faultstring: '格式化程序抛出错误试图反序列化消息时出现异常:有一个错误试图反序列化参数http://tempuri.org/:loc。的InnerException消息为"第1行位置522出错"。元素'http://tempuri.org/:loc'包含映射到的类型的数据名字"http://www.w3.org/2001/XMLSchema位置"。反序列化器有不知道任何映射到此名称的类型。考虑使用DataContractResolver或添加与'Location'对应的类型到已知类型的列表—例如,通过使用KnownTypeAttribute属性或将其添加到传递给的已知类型列表中DataContractSerializer。"。详情请参见InnerException。faultactor: 'null' detail: org.kxml2.kdom.Node@4053aa28
理想情况下,我不需要将LocationLibrary类移动到主WCF项目中,因为这会使期望它在不同名称空间中的遗留客户端的处理复杂化。但是,我确实通过将这些类移动到主WCF项目中并修改java中的marshal类来完成工作:
public void register(SoapSerializationEnvelope cm) {
cm.addMapping("http://schemas.datacontract.org/2004/07/MagicSauceV3", "Location", Location.class, this);
}
public void writeInstance(XmlSerializer writer, Object obj) throws IOException {
Location loc = (Location)obj;
writer.startTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "latitude");
writer.text(Double.toString(loc.latitude));
writer.endTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "latitude");
writer.startTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "longitude");
writer.text(Double.toString(loc.longitude));
writer.endTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "longitude");
}
现在我得到了工作,我只是让它与LocationLibrary WCF类一起工作(在一个单独的DLL中)。因此,我将marshal类修改为:
public void register(SoapSerializationEnvelope cm) {
cm.addMapping("http://schemas.datacontract.org/2004/07/LocationLibrary", "Location", Location.class, this);
}
public void writeInstance(XmlSerializer writer, Object obj) throws IOException {
Location loc = (Location)obj;
writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude");
writer.text(Double.toString(loc.latitude));
writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude");
writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude");
writer.text(Double.toString(loc.longitude));
writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude");
}
这看起来应该会成功,因为它产生了与WP7版本相似的XML。
WP7工作XML请求:
<GetData xmlns="http://tempuri.org/">
<deviceId>{valid GUID}</deviceId>
<appId>{valid GUID}</appId>
<devKey>{valid GUID}</devKey>
<loc xmlns:d4p1="http://schemas.datacontract.org/2004/07/LocationLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:latitude>47.65</d4p1:latitude>
<d4p1:longitude>-122.34</d4p1:longitude>
</loc>
</GetData>
Android无效XML请求:
<GetData xmlns="http://tempuri.org/" id="o0" c:root="1">
<deviceId i:type="d:UUID">{valid GUID}</deviceId>
<appId i:type="d:UUID">{valid GUID}</appId>
<devKey i:type="d:UUID">{valid GUID}</devKey>
<loc i:type="n0:Location" xmlns:n0="http://schemas.datacontract.org/2004/07/LocationLibrary">
<n0:latitude>47.65</n0:latitude>
<n0:longitude>-122.34</n0:longitude>
</loc>
</GetData>
上面的Android XML请求产生如下响应错误:
08-05 22:51:23.703: WARN/System.err(1382): SoapFault - faultcode: 'a:DeserializationFailed' faultstring: '格式化器在尝试反序列化消息时抛出异常:在尝试反序列化参数http://tempuri.org/:loc时出现错误。InnerException消息是"第1行位置590错误"。元素'http://tempuri.org/:loc'包含映射到名称'http://schemas.datacontract.org/2004/07/LocationLibrary:Location'的类型的数据。反序列化程序不知道映射到该名称的任何类型。考虑使用DataContractResolver或将与'Location'相对应的类型添加到已知类型列表中——例如,通过使用KnownTypeAttribute属性或将其添加到传递给DataContractSerializer的已知类型列表中。详情请参见InnerException。' faulfactor: 'null' detail: org.kxml2.kdom.Node@4054bcb8
唯一值得注意的区别是在工作XML请求中的"loc"属性:xmlns:我= " http://www.w3.org/2001/XMLSchema-instance "
我不知道这是否会有什么不同(它似乎没有被嵌套标签引用)。我也不知道如何在不搞乱另一个命名空间的情况下添加额外的命名空间。
很抱歉写了这么长一篇文章,但我想确保你有你需要帮助的所有信息:)
谢谢!
正如我所料,你只是在做ToString
,但这是错误的。您的位置不像两个连接值的字符串那样传输。它作为对象序列化成XML传输。比如:
<Location> <!-- or loc -->
<latitude>10.0</latitude>
<longitude>-20.0</longitude>
</Location>
感谢Ladislav指出了正确的方向!我不知道这里有什么礼仪来奖励这一点,但如果有人能给我一些建议,我很乐意遵循。
答案是双重的,我可以确认它现在在一个单独的DLL中与LocationLibrary的Location类一起工作:
-
改变我在Android中使用我的封送类形成XML请求的方式。writeInstance方法只使用XmlSerializer类的. textmethod,调用Location类的. tostring()。我对它进行了修改,以利用形成XML标记的正确方法。startTag和。endtag方法):
public void writeInstance(XmlSerializer writer, Object obj) throws IOException { Location loc = (Location)obj; writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude"); writer.text(Double.toString(loc.latitude)); writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude"); writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude"); writer.text(Double.toString(loc.longitude)); writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude"); }
-
为WCF服务添加一些简单的标签。我将[DataContract]添加到WCF服务使用的LocationLibrary程序集中的Location类中,并将[datammember]添加到它的每个成员中:
[DataContract] public class Location { [DataMember] public double latitude { get; set; } [DataMember] public double longitude { get; set; } public new string ToString() { return latitude.ToString() + "," + longitude.ToString(); } public bool Equals(Location loc) { return this.latitude == loc.latitude && this.longitude == loc.longitude; }