'Using python and suds, data not read by server side because element is not defined as an array

I am a very inexperienced programmer with no formal education. Details will be extremely helpful in any responses.

I have made several basic python scripts to call SOAP APIs, but I am running into an issue with a specific API function that has an embedded array.

Here is a sample excerpt from a working XML format to show nested data:

<bomData xsi:type="urn:inputBOM" SOAP-ENC:arrayType="urn:bomItem[]">
                <bomItem>
                    <item_partnum></item_partnum>
                    <item_partrev></item_partrev>
                    <item_serial></item_serial>
                    <item_lotnum></item_lotnum>
                    <item_sublotnum></item_sublotnum>
                    <item_qty></item_qty>
                </bomItem>
                <bomItem>
                    <item_partnum></item_partnum>
                    <item_partrev></item_partrev>
                    <item_serial></item_serial>
                    <item_lotnum></item_lotnum>
                    <item_sublotnum></item_sublotnum>
                    <item_qty></item_qty>
                </bomItem>
            </bomData>

I have tried 3 different things to get this to work to no avail.

I can generate the near exact XML from my script, but a key attribute missing is the 'SOAP-ENC:arrayType="urn:bomItem[]"' in the above XML example.

Option 1 was using MessagePlugin, but I get an error because my section is like the 3 element and it always injects into the first element. I have tried body[2], but this throws an error.

Option 2 I am trying to create the object(?). I read a lot of stack overflow, but I might be missing something for this.

Option 3 looked simple enough, but also failed. I tried setting the values in the JSON directly. I got these examples by an XML sample to JSON.

I have also done a several other minor things to try to get it working, but not worth mentioning. Although, if there is a way to somehow do the following, then I'm all ears: bomItem[]: bomData = {"bomItem"[{...,...,...}]}

Here is a sample of my script:

# for python 3
# using pip install suds-py3
from suds.client import Client
from suds.plugin import MessagePlugin
# Config

#option 1: trying to set it as an array using plugin
class MyPlugin(MessagePlugin):
  def marshalled(self, context):
     body = context.envelope.getChild('Body')
     bomItem = body[0]
     bomItem.set('SOAP-ENC:arrayType', 'urn:bomItem[]')

URL = "http://localhost/application/soap?wsdl"
client = Client(URL, plugins=[MyPlugin()])

transact_info = { 
            "username":"",
            "transaction":"",
            "workorder":"",
            "serial":"",
            "trans_qty":"",
            "seqnum":"",
            "opcode":"",
            "warehouseloc":"",
            "warehousebin":"",
            "machine_id":"",
            "comment":"",
            "defect_code":""
            }

#WIP - trying to get bomData below working first
inputData = {
        "dataItem":[
            {
            "fieldname": "",
            "fielddata": ""
            }
        ]
    }

#option 2: trying to create the element here and define as an array
#inputbom = client.factory.create('ns3:inputBOM')
#inputbom._type = "SOAP-ENC:arrayType"
#inputbom.value = "urn:bomItem[]"

bomData = {
       #Option 3: trying to set the time and array type in JSON
       #"@xsi:type":"urn:inputBOM",
       #"@SOAP-ENC:arrayType":"urn:bomItem[]",
       "bomItem":[
          {
             "item_partnum":"",
             "item_partrev":"",
             "item_serial":"",
             "item_lotnum":"",
             "item_sublotnum":"",
             "item_qty":""
          },
          {
             "item_partnum":"",
             "item_partrev":"",
             "item_serial":"",
             "item_lotnum":"",
             "item_sublotnum":"",
             "item_qty":""
          }
       ]
    }

try:
    response = client.service.transactUnit(transact_info,inputData,bomData)
    print("RESPONSE: ")
    print(response)
    #print(client)
    #print(envelope)

except Exception as e:
    #handle error here
    print(e)

I appreciate any help and hope it is easy to solve.



Solution 1:[1]

I have found the answer I was looking for. At least a working solution.

In any case, option 1 worked out. I read up on it at the following link: https://suds-py3.readthedocs.io/en/latest/ You can review at the '!MessagePlugin' section.

I found a solution to get message plugin working from the following post: unmarshalling Error: For input string: ""

A user posted an example how to crawl through the XML structure and modify it.

Here is my modified example to get my script working:

#Using MessagePlugin to modify elements before sending to server
class MyPlugin(MessagePlugin):

    # created method that could be reused to modify sections with similar 
    # structure/requirements
    def addArrayType(self, dataType, arrayType, transactUnit):
        # this is the code that is key to crawling through the XML - I get 
        # the child of each parent element until I am at the right level for 
        # modification
        data = transactUnit.getChild(dataType)
        if data:
            data.set('SOAP-ENC:arrayType', arrayType)

    def marshalled(self, context):
        # Alter the envelope so that the xsd namespace is allowed
        context.envelope.nsprefixes['xsd'] = 'http://www.w3.org/2001/XMLSchema'
        body = context.envelope.getChild('Body')
        transactUnit = body.getChild("transactUnit")
        if transactUnit:
            self.addArrayType('inputData', 'urn:dataItem[]', transactUnit)
            self.addArrayType('bomData', 'urn:bomItem[]', transactUnit)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Crusnik02