How to get unparsed XML from a suds response, and best django model field to use for storage
Asked Answered
C

2

4

I am using suds to request data from a 3rd party using a wsdl. I am only saving some of the data returned for now, but I am paying for the data that I get so I would like to keep all of it. I have decided that the best way to save this data is by capturing the raw xml response into a database field both for future use should I decide that I want to start using different parts of the data and as a paper trail in the event of discrepancies.

So I have a two part question:

Is there a simple way to output the raw received xml from the suds.client object? In my searches for the answer to this I have learned this can be done through logging, but I was hoping to not have to dig that information back out of the logs to put into the database field. I have also looked into the MessagePlugin.recieved() hook, but could not really figure out how to access this information after it has been parsed, only that I can override that function and have access to the raw xml as it is being parsed (which is before I have decided whether or not it is actually worth saving yet or not). I have also explored the retxml option but I would like to use the parsed version as well and making two separate calls, one as retxml and the other parsed will cost me twice. I was hoping for a simple function built into the suds client (like response.as_xml() or something equally simple) but have not found anything like that yet. The option bubbling around in my head might be to extend the client object using the .received() plugin hook that saves the xml as an object parameter before it is parsed, to be referenced later... but the execution of such seems a little tricky to me right now, and I have a hard time believing that the suds client doesn't just have this built in somewhere already, so I thought I would ask first.

The other part to my question is: What type of django model field would be best suited to handle up to ~100 kb of text data as raw xml? I was going to simply use a simple CharField with a stupidly long max_length, but that feels wrong.

Thanks in advance.

Checkrow answered 8/10, 2014 at 21:33 Comment(0)
C
8

I solved this by using the flag retxml on client initialization:

client = Client(settings.WSDL_ADDRESS, retxml=True)
raw_reply = client.service.PersonSearch(soapified_search_object)

I was then able to save raw_reply as the raw xml into a django models.TextField() and then inject the raw xml to get a suds parsed result without having to re-submit my search lika so:

parsed_result = client.service.PersonSearch(__inject={'reply': raw_reply})

I suppose if I had wanted to strip off the suds envelope stuff from raw reply I could have used a python xml library for further usage of the reply, but as my existing code was already taking the information I wanted from the suds client result I just used that.

Hope this helps someone else.

Checkrow answered 9/10, 2014 at 16:44 Comment(1)
I realize this post is almost 7 years old at this point but which version of suds supports the __inject argument?Settlings
I
6

I have used kyrayzk solution for a while, but have always found it a bit hackish, as I had to create a separate dummy client just for when I needed to process the raw XML. So I sort of reimplemented .last_received() and .last_sent() methods (which were (IMHO, mistakenly) removed in suds-jurko 0.4.1) through a MessagePlugin.

Hope it helps someone:

class MyPlugin(MessagePlugin):
    def __init__(self):
        self.last_sent_raw = None
        self.last_received_raw = None

    def sending(self, context):
        self.last_sent_raw = str(context.envelope)

    def received(self, context):
        self.last_received_raw = str(context.reply)

Usage:

plugin = MyPlugin()
client = Client(TRTH_WSDL_URL, plugins=[plugin])
client.service.SendSomeRequest()

print plugin.last_sent_raw
print plugin.last_received_raw

And as an extra, if you want a nicely indented XML, try this:

from lxml import etree
def xmlpprint(xml):
    return etree.tostring(etree.fromstring(xml), pretty_print=True)
Ianthe answered 20/4, 2016 at 3:11 Comment(4)
I'm not sure how is this supposed to work. If I have an error in the client.service.SendSomeRequest() line, the message will not be printed as the last two lines do not get executed? I tried with try -> except but it didn't work either, any ideas?Mig
Without try/except the print statements will obviously fail, but the messages may still have been successfully intercepted by the plugin, depending on where exactly inside SendSomeRequest() the error occurred. MessagePlugin provides 5 "hooks" for message interception, with sending and last being #2 and #3 being respectively (see documentation: fedorahosted.org/suds/wiki/Documentation#MessagePlugin). Perhaps changing sending to marshalled may solve your problem.Ianthe
OK @Gustavo, and what about the context? Before, I had a different plugin, that I've taken from Zhen Tian, and it did not require a context argument. For example, I was able to do this: session=logging_plugin.last_received().getChild("soapenv:Envelope").getChild("soapenv:Header"). How do I get it done now?Mig
Nothing stops your from modifying the about plugin and adding a method to return self.last_received_raw (like Zhen Tian's plugin - which is mostly the exact same as mine, btw). Simply accessing the attribute as plugin.last_received_raw is pretty much the same though.Ianthe

© 2022 - 2024 — McMap. All rights reserved.