A better XML Serializer for Python 3 - python-3.x

I tried xml_marshaller as follows:
from xml_marshaller import xml_marshaller
class Person:
firstName = "John"
lastName = "Doe"
person1 = Person()
strXmlPerson = xml_marshaller.dumps(person1);
print(strXmlPerson)
The output from above is:
b'<marshal><object id="i2" module="__main__" class="Person"><tuple/><dictionary id="i3"><string>firstName</string><string>John</string><string>lastName</string><string>Doe</string></dictionary></object></marshal>'
which when formatted looks like this, which in my opinion is the ugliest XML possible:
b'<marshal>
<object id="i2" module="__main__" class="Person">
<tuple/>
<dictionary id="i3">
<string>firstName</string>
<string>John</string>
<string>lastName</string>
<string>Doe</string>
</dictionary>
</object>
</marshal>'
What is the b and quotes doing there? Means "binary" maybe? Is that really part of the data, or just a side effect of printing it to the console?
Is there any Python 3 library that will create something more closer to "human" like this:
<Person>
<firstname>John</firstname>
<lastname>Doe<lastname>
</Person>
I'm looking for something close to what .NET creates (see http://mylifeismymessage.net/xml-serializerdeserializer/.
Please don't tell me try JSON or YAML, that's not the question. I might want to run the file through XSLT for example.
Update 2 days later:
I like the Peter Hoffman answer here:
How can I convert XML into a Python object?
person1 = Person("John", "Doe")
#strXmlPerson = xml_marshaller.dumps(person1);
person = objectify.Element("Person")
strXmlPerson = lxml.etree.tostring(person1, pretty_print=True)
print(strXmlPerson)
gives error:
TypeError: Type 'Person' cannot be serialized.
In my scenario I might already have a class structure, and don't want to switch to they way they are doing it You can I serialize my "Person" class?

The reason the output is showing xml as a dictionary is most likely because the properties don't have a reference to the class. You should consider using self. and assigning values within a __init__ function.
class Person:
def __init__(self):
self.firstName = "John"
self.lastName = "Doe"
There are many ways to convert an object to XML. However try using the package dicttoxml. As the name suggest you'll need to convert object to a dictionary, which can be done using vars().
The full solution:
from dicttoxml import dicttoxml
class Person:
def __init__(self):
self.firstName = "John"
self.lastName = "Doe"
person = vars(Person()) # vars is pythonic way of converting to dictionary
xml = dicttoxml(person, attr_type=False, custom_root='Person') # set root node to Person
print(xml)
Output:
b'<?xml version="1.0" encoding="UTF-8" ?><Person><firstName>John</firstName><lastName>Doe</lastName></Person>'
If you want to format the XML nicely, then you can use the built in xml.dom.minidom.parseString library.
from dicttoxml import dicttoxml
from xml.dom.minidom import parseString
class Person:
def __init__(self):
self.firstName = "John"
self.lastName = "Doe"
person = vars(Person()) # vars is pythonic way of converting to dictionary
xml = dicttoxml(person, attr_type=False, custom_root='Person') # set root node to Person
print(xml)
dom = parseString(xml)
print(dom.toprettyxml())
Output:
<?xml version="1.0" ?>
<Person>
<firstName>John</firstName>
<lastName>Doe</lastName>
</Person>
Do check out the documentation https://pypi.org/project/dicttoxml/ as you can pass additional arguments to alter the output.

Related

Unsuccessful in trying to convert a column of strings to integers in Python (hoping to sort)

I am attempting to sort a dataframe by a column called 'GameId', which are currently of type string and when I attempt to sort the result is unexpected. I have tried the following but still return a type string.
TEST['GameId'] = TEST['GameId'].astype(int)
type('GameId')
One way to make the data life easier is using dataclasses!
from dataclasses import dataclass
# here will will be calling the dataclass decorator to send hints for data type!
#dataclass
class Columns:
channel_id : int
frequency_hz : int
power_dBmV : float
name : str
# this class will call the data class to organise the data as data.frequency data.power_dBmV etc
class RadioChannel:
radio_values = ['channel_id', 'frequency', 'power_dBmV']
def __init__(self, data): # self is 'this' but for python, it just means that you mean to reference 'this' or self instance
self.data = data # this instances data is called data here
data = Columns(channel_id=data[0], frequency=data[1], power_dBmv=data[4], name=data[3]) # now we give data var a val!
def present_data(self):
# this is optional class method btw
from rich.console import Console
from rich.table import Table
console = Console()
table = Table(title="My Radio Channels")
for item in self.radio_values:
table.add_column(item)
table.add_row(data.channel_id, data.frequency_hz, data.power_dBmv)
console.print(table)
# ignore this if its confusing
# now inside your functional part of your script
if __name__ == '__main__':
myData = []
# calling an imaginary file here to read
with open("my_radio_data_file", 'r') as myfile:
mylines = myfile.readlines()
for line in myline:
myData.append(line)
myfile.close()
#my data would look like a string ["value", value, 00, 0.0, "hello joe, from: world"]
ch1 = radioChannel(data=myData[0])
ch1.present_data()
This way you can just call the class object on each line of a data file. and print it to see if it lines up. once you get the hang of it, it starts to get fun.
I used rich console here, but it works well with pandas and normal dataframes!
dataclasses help the interpreter find its way with type hints and class structure.
Good Luck and have fun!

XML parsing with a Class call

I parsed an xml file with xml.etree python module. Its Working well, but now I try to call this code as a module/Class from a main program. I would like to send the xml tree and a filename for the csv writing to the Class.
My dummy file to call the file with the Class:
import xml.etree.ElementTree as ET
tree = ET.ElementTree(file='phonebook.xml')
root = tree.getroot()
from xml2csv import Vcard_xml2csv
my_export = Vcard_xml2csv(tree, 'phonebook.csv')
my_export.write_csv()
here is the class:
class Vcard_xml2csv:
"""The XML phone book export to a csv file"""
def __init__(self, tree, csvfilename):
root = tree.getroot()
self.csvfilename = csvfilename
def write_content(contact, csvfilename):
with open(csvfilename, mode='a+') as phonebook_file:
contact_writer = csv.writer(phonebook_file, delimiter=',', quotechar=" ", quoting=csv.QUOTE_MINIMAL)
contact_writer.writerow([contact]) # delete [] if you will see only text separated by a comma
def write_csv(tree):
for contacts in tree.iter(tag='phonebook'):
for contact in contacts.findall("./contact"):
row=[]
for category in contact.findall("./category"):
print('Category: ',category.text)
category=category.text
row.append(category)
for person in contact.findall("./person/realName"):
print('realName: ',person.text)
realName=person.text
row.append(realName)
for mod_time in contact.findall("./mod_time"):
print ('mod_time: ', mod_time.text)
mod_time=mod_time.text
row.append(mod_time)
for uniqueid in contact.findall("./uniqueid"):
print ('uniqueid: ', uniqueid.text)
uniqueid_=uniqueid.text
row.append(uniqueid_)
numberlist=[]
for number in contact.findall("./telephony/number"):
print('id',number.attrib['id'],'type:',number.attrib['type'], 'prio:',number.attrib['prio'], 'number: ',number.text)
id_=number.attrib['id']
numberlist.append(id_)
type_=number.attrib['type']
numberlist.append(type_)
prio_=number.attrib['prio']
numberlist.append(prio_)
number_=number.text
numberlist.append(number_)
contact = row + numberlist
write_content(contact, csvfilename)
numberlist=[]
Iam geeting the ERROR below:
for contacts in tree.iter(tag='phonebook'): AttributeError:
'Vcard_xml2csv' object has no attribute 'iter' Thanks for your help!
When we define a method in a class, like write_csv() in the example, the first parameter is always the class instance. Think of it as a way to access class attributes and methods. Conventionally, for readability, the first parameter is called self.
In the write_csv method, tree has become this class instance and that is the reason you see the error. The resolution to this would be to define the method like the following:
def write_csv(self, tree)
....
....
and the call to the method would be:
my_export.write_csv(tree)
I hope this helps. More about self here

Python Dataclass - getting metadata from attribute name

I have a Dataclass that looks like this:
#dataclass
class Example:
smtng: int = field(init=True, metadata={'int_name':"Thing",'ext_name':"it"})
smtng_else: str = field(init=True, metadata={'int_name':"other",'ext_name':"that"})
as you can see the metadata dict has external and internal name fields
I would like to access these through functions E.g. get_ext_name(attribute_name) -> which would return an attribute's name under the metadata dict "ext_name"
is there a sleek way to to this?
Thanks
So I found this way to do it thanks to vibhu4agarwal on GeeksForGeeks.
Adding this method to the class works:
def ext_name(self, attribute_name):
return self.__dataclass_fields__[attribute_name].metadata['ext_name'])
if there's a better workaround I'd love to see it
here's a link to the article:
https://www.geeksforgeeks.org/data-classes-in-python-set-3-dataclass-fields/

how to retrieve text from an xml

I want to try an retrieve the name of a hotel (in this example the hotel is called 'Test Hotel'), but I am unsure how to do it because I believe to get a #node it's using # but how do I retrieve a text?
Below is the xml:
<xxx xmlns:soap="xxx" xmlns:xsi="xxx" xmlns:xsd="xxx">
<xxx>
<xxxxmlns="xxx">
<AvailabilityRS Url="xxx" IntCode="xxx">
<Results>
<HotelResult Code="xxx"DestinationZone="xxx">
<HotelInfo>
<Name>Test Hotel</Name>
Below is the script:
def response = testRunner.testCase.getTestStepByName("xxx").getProperty("Response").getValue()
def parsedxml = new XmlSlurper().parseText(response)
def hotelName = parsedxml.'soap:Body'.HotelAvailResponse[0].xxx[0].Results[0].xxxx[0].xxx[0].Name[0].toString()
Instead of using toString(), you should be able to just use text()
def hotelName = parsedxml.Body
.HotelAvailResponse
.AvailabilityRS
.Results
.HotelResult
.HotelInfo
.Name.text()

How to convert a #ToString generated string back to object with Groovy

For example, instance of the following following class produces string A(x:7, values:[hello, world])
#ToString( includeNames=true )
class A {
def x
def values = []
}
How can I transform this String back to an instance of the class?
Based on the comments by #cfrick, this is not possible.
(Answering my own question for the sake of closing this topic.)

Resources