简体   繁体   中英

Issue executing Java web service from Python using suds and protobuf with byte array parameters

I am using Protobuf objects for serialization, as we need to connect from Java, CSharp and Python clients to a Java web service.

If I write Java protobuf bytes to a file, I am able to read in Python by simply doing:

f = open("../../../../resources/test.protoBytes", "rb")
java_deserialized = timeSeriesRequests.TimeSeriesRequestMetaData()
java_deserialized.ParseFromString(f.read())

If I write from Python to a file:

temp = time_series_request_meta_data.SerializeToString()
f = open("../../../../resources/test.protoBytes", "wb")
f.write(temp)
f.close()

If have no problem reading in Java.

Using suds-jurko 0.6 I attempt to call a SOAP service which takes byte[] in Java by doing:

client = Client('http://localhost:5750/ws/protoservice?wsdl', plugins=[LogPlugin()])
response = client.service.addValueToTimeSeries(data.SerializeToString(), otherData.SerializeToString())

Server side method signature:

public byte[] addValueToTimeSeries(byte[] protoRequestMetaData, byte[] protoRequest)

I can put a break point on the server side call, and the method is called, but the byte array string is incorrect.

This was sent from the client:

b'<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://services.common.ddp/"><SOAP-ENV:Header/><ns0:Body><ns1:addValueToTimeSeries><arg0>b&apos;\\n\\x06ddp_sm\\x12\\x0btest-domain\\x1a\\tlocalhost&apos;</arg0><arg1>b&apos;\\n\\x06\\x08\\x81\\x9c\\x92\\xa5\\x05\\x12\\x06\\x08\\x81\\x9c\\x92\\xa5\\x05\\x1a\\x04Test&quot;\\x05Test2*&lt;\\n/type.googleapis.com/google.protobuf.DoubleValue\\x12\\t\\t\\x00\\x00\\x00\\x00\\x00\\x00Y@&apos;</arg1></ns1:addValueToTimeSeries></ns0:Body></SOAP-ENV:Envelope>'

I tried doing this instead:

response = client.service.addValueToTimeSeries(list(data.SerializeToString()), list(otherData.SerializeToString())

and I can see that in the client the correct byte array is created, but now on the server side the byte array is empty.

This is sent from the Python client (from logging in suds):

b'<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://services.common.ddp/"><SOAP-ENV:Header/><ns0:Body><ns1:addValueToTimeSeries><arg0>10</arg0><arg0>6</arg0><arg0>100</arg0><arg0>100</arg0><arg0>112</arg0><arg0>95</arg0><arg0>115</arg0><arg0>109</arg0><arg0>18</arg0><arg0>11</arg0><arg0>116</arg0><arg0>101</arg0><arg0>115</arg0><arg0>116</arg0><arg0>45</arg0><arg0>100</arg0><arg0>111</arg0><arg0>109</arg0><arg0>97</arg0><arg0>105</arg0><arg0>110</arg0><arg0>26</arg0><arg0>9</arg0><arg0>108</arg0><arg0>111</arg0><arg0>99</arg0><arg0>97</arg0><arg0>108</arg0><arg0>104</arg0><arg0>111</arg0><arg0>115</arg0><arg0>116</arg0><arg1>10</arg1><arg1>6</arg1><arg1>8</arg1><arg1>129</arg1><arg1>156</arg1><arg1>146</arg1><arg1>165</arg1><arg1>5</arg1><arg1>18</arg1><arg1>6</arg1><arg1>8</arg1><arg1>129</arg1><arg1>156</arg1><arg1>146</arg1><arg1>165</arg1><arg1>5</arg1><arg1>26</arg1><arg1>4</arg1><arg1>84</arg1><arg1>101</arg1><arg1>115</arg1><arg1>116</arg1><arg1>34</arg1><arg1>5</arg1><arg1>84</arg1><arg1>101</arg1><arg1>115</arg1><arg1>116</arg1><arg1>50</arg1><arg1>42</arg1><arg1>60</arg1><arg1>10</arg1><arg1>47</arg1><arg1>116</arg1><arg1>121</arg1><arg1>112</arg1><arg1>101</arg1><arg1>46</arg1><arg1>103</arg1><arg1>111</arg1><arg1>111</arg1><arg1>103</arg1><arg1>108</arg1><arg1>101</arg1><arg1>97</arg1><arg1>112</arg1><arg1>105</arg1><arg1>115</arg1><arg1>46</arg1><arg1>99</arg1><arg1>111</arg1><arg1>109</arg1><arg1>47</arg1><arg1>103</arg1><arg1>111</arg1><arg1>111</arg1><arg1>103</arg1><arg1>108</arg1><arg1>101</arg1><arg1>46</arg1><arg1>112</arg1><arg1>114</arg1><arg1>111</arg1><arg1>116</arg1><arg1>111</arg1><arg1>98</arg1><arg1>117</arg1><arg1>102</arg1><arg1>46</arg1><arg1>68</arg1><arg1>111</arg1><arg1>117</arg1><arg1>98</arg1><arg1>108</arg1><arg1>101</arg1><arg1>86</arg1><arg1>97</arg1><arg1>108</arg1><arg1>117</arg1><arg1>101</arg1><arg1>18</arg1><arg1>9</arg1><arg1>9</arg1><arg1>0</arg1><arg1>0</arg1><arg1>0</arg1><arg1>0</arg1><arg1>0</arg1><arg1>0</arg1><arg1>89</arg1><arg1>64</arg1></ns1:addValueToTimeSeries></ns0:Body></SOAP-ENV:Envelope>'

Can someone please suggest what I need to do?

FYI, this is my first Python application, hence I wouldn't be surprised if the answer is trivial. It seems very strange to me that this works if done via files, but fails when done across the web service.

I've now made a small example web service, but can't upload zip it appears.

I have tried using struct.unpack like this:

struct.unpack('B' * len(serialized_request), serialized_request)

This returns the correct int array but is passed through to the web service as empty array.

Below is the contents of the wsdl:

<definitions targetNamespace="http://examples.com/" name="com.examples.ExampleWebService"><types><xsd:schema><xsd:import namespace="http://examples.com/" schemaLocation="http://localhost:5750/ws/examplewebservice?xsd=1"/></xsd:schema></types><message name="addRequest"><part name="parameters" element="tns:addRequest"/></message><message name="addRequestResponse"><part name="parameters" element="tns:addRequestResponse"/></message><portType name="ExampleWebServiceContract"><operation name="addRequest"><input wsam:Action="http://examples.com/ExampleWebServiceContract/addRequestRequest" message="tns:addRequest"/><output wsam:Action="http://examples.com/ExampleWebServiceContract/addRequestResponse" message="tns:addRequestResponse"/></operation></portType><binding name="ExampleWebServicePortBinding" type="tns:ExampleWebServiceContract"><soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/><operation name="addRequest"><soap:operation soapAction=""/><input><soap:body use="literal"/></input><output><soap:body use="literal"/></output></operation></binding><service name="com.examples.ExampleWebService"><port name="ExampleWebServicePort" binding="tns:ExampleWebServicePortBinding"><soap:address location="http://localhost:5750/ws/examplewebservice"/></port></service></definitions>

After trying many combinations, I found that to pass a byte array to suds from Python using Protobuf you need to do the following steps:

  1. Call SerializeToString() on Protobuf object
  2. Pass the string representation of bytes to base64.b64encode (ie import base64 module)
  3. Call decode() on the result which puts back to ASCII stripping off the b' at the start and the ' at the end.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM