📜 ⬆️ ⬇️

We work with a 1C web service from an application on Android

When working on the front for a cafe , the task appeared to access the 1C web service from an application developed on Android. Google gave me a few answers on how to work with SOAP in general, using the ksoap2-android library. They helped in the transfer of simple types, but when it came to transferring an array, I had to think a little.

Web service on the side of 1C


In the 1C configuration, a web service was created with the WriteSale method. The method takes several parameters, one of which, items, is of type ItemsSold (specified in the XDTO configuration package). The remaining parameters are of simple types (string, datetime). Configuration screen:


')
The ItemsSold type has the only Items property, for which the Maximum Quantity property is set to -1, indicating that it is an array. The type of this property is ItemSold. Screen:



ItemSold type has all properties of a simple type. The Web service's WriteSale method has the following code:

 WriteSale(id, date, clientCardNumber, discountRate, items, deptId, bonuses, premiumBonuses)  = "OK";   = (clientCardNumber);  = (discountRate);  = (deptId); //...  = (date, (id), );   ()   = ..(); . = date; . = id;   = .(); ; . = (bonuses); //... ..(); //       item  items.Items   = (item.Code);  = ..(); . = (item.Quantity); //... ; .(.);   = (); ("Cafe.WriteSale - : " + , .); ; ; //     ""  ;  


Android client



To access a web service from an Android application, I wrote the following code (in accordance with a good example of a simple client ):

 protected String call() throws Exception { result = null; HttpTransportSE httpTransport = new HttpTransportSE(uri); httpTransport.debug = true; String resultString; SoapObject request = new SoapObject(namespace, methodName); request.addProperty("id", sale.getId()); SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss"); request.addProperty("date", dateFormat.format(sale.getDate())); request.addProperty("clientCardNumber", sale.getCardNumber()); request.addProperty("bonuses", Double.toString(sale.getBonuses())); //... // see - http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks#Adding_an_array_of_complex_objects_to_the_request SoapObject sales = new SoapObject(namespace, "items"); for (SaleItemInformation item : sale.getSales()) { SoapObject itemSoap = new SoapObject(namespace, "Items"); itemSoap.addProperty("Code", item.getItem().getSourceCode()); itemSoap.addProperty("Quantity", Double.toString(item.getQuantity())); //... sales.addSoapObject(itemSoap); } request.addSoapObject(sales); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope( SoapEnvelope.VER11); //    -       xml envelope.implicitTypes = true; envelope.setOutputSoapObject(request); try { httpTransport.call(soapAction, envelope); } catch (Exception e) { e.printStackTrace(); throw e; } resultString = envelope.getResponse().toString(); return resultString; } 


It seems that the code looks right, forms a beautiful xml request:

 <v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/"> <v:Header /> <v:Body> <n0:WriteSale id="o0" c:root="1" xmlns:n0="http://www.xxxxx.ru"> <date i:type="d:string">Thu May 31 16:13:08 YEKST 2012</date> <clientCardNumber i:type="d:string">120</clientCardNumber> <discountRate i:type="d:string">5.0</discountRate> <id i:type="d:long">11</id> <n0:items i:type="n0:items"> <n0:Items i:type="n0:Items"> <Code i:type="d:string">3000</Code> <Price i:type="d:string">100.0</Price> <Quantity i:type="d:string">2.0</Quantity> <Sum i:type="d:string">200.0</Sum> </n0:Items> <n0:Items i:type="n0:Items"> <Code i:type="d:string">3001</Code> <Price i:type="d:string">110.0</Price> <Quantity i:type="d:string">1.0</Quantity> <Sum i:type="d:string">110.0</Sum> </n0:Items> </n0:items> </n0:WriteSale> </v:Body> </v:Envelope> 


But the web service responds to it with the 500th error. At the same time, referring to another method with parameters of a simple type on the same web service, we get the correct answer. Moreover, when accessing from another database 1C via a WS-link to the above web service method, we get the correct answer and the necessary actions on the web service side. Therefore it was necessary to intercept the request formed by other base 1C. It was not possible to make this a fidler , since it somehow cut the request body itself with xml and did not pass it to the web service. It was normal to intercept the request only with the help of WireShark . So, the request text from 1C:

 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header/> <soap:Body> <m:WriteSale xmlns:m="http://www.xxxxx.ru"> <m:id xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1</m:id> <m:date xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2</m:date> <m:clientCardNumber xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3</m:clientCardNumber> <m:discountRate xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4</m:discountRate> <m:items xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <m:Items> <m:Code>123</m:Code> <m:Price>12.2</m:Price> <m:Quantity>2</m:Quantity> <m:Sum>2</m:Sum> </m:Items> <m:Items> <m:Code>2</m:Code> <m:Price>1</m:Price> <m:Quantity>2</m:Quantity> <m:Sum>2</m:Sum> </m:Items> </m:items> </m:WriteSale></soap:Body> </soap:Envelope> 


It is easy to see that for nested elements of arrays (Code, Price ...) the ksoap2-android library does not prefix with the namespace. For the root elements (id, date ...) they are also not stamped, but this fact does not enter 1C into a stupor. And their lack of sub-elements causes the program to question the correctness of the input data, it can not read them.

Having studied the library code, I decided that the most rational would be to modify the SoapObject # addProperty (String, Object) method as follows:

 public static class SoapObjectCustom extends SoapObject { public SoapObjectCustom(String namespace, String name) { super(namespace, name); } @Override public SoapObject addProperty(String name, Object value) { PropertyInfo propertyInfo = new PropertyInfo(); propertyInfo.name = name; propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass(); propertyInfo.setValue(value); //    propertyInfo.setNamespace(this.namespace); return addProperty(propertyInfo); } } 


In the source code, I replaced the SoapObject objects with SoapObjectCustom in the following places:

 //... SoapObjectCustom request = new SoapObjectCustom(namespace, methodName); //... SoapObject sales = new SoapObject(namespace, "items"); for (SaleItemInformation item : sale.getSales()) { SoapObjectCustom itemSoap = new SoapObjectCustom(namespace, "Items"); //... } //... 


Conclusion



Most likely, it makes sense that the authors did not include namespace prefixes in the properties of the elements. And it is quite possible that in working with other web services such adjustments will lead to incorrect program behavior. Nevertheless, this method works with 1C web services, I hope this description will help someone in their work.

The above was tested with 1C v.8.2.15.294 and Android 12 (3.0).

Source: https://habr.com/ru/post/145389/


All Articles