শিমুল চৌধুরী

পাইথন এ XML ডকুমেন্ট তৈরী করার একটি ভালো পদ্ধতি

XML ডকুমেন্ট Parse করা বা তৈরী করার সময়, অথবা SOAP এপিআই নিয়ে কাজ করার সময় আমরা সাধারণত xml.etree.ElementTree বা xml.dom.minidom ব্যবহার করে থাকি। এই এপিআই গুলো XML নিয়ে কাজ করার জন্য খুবই শক্তিশালী এবং প্রায় সবধরণের XML সংক্রান্ত কাজ এদের মাধ্যমে করা যায়। কিন্তু আমার মনে হয় না এপিআই গুলো সহজবোধ্য। এদের মাধ্যমে কাজ করাতে কোনো মজা নেই, উল্টো বড় ডকুমেন্ট এ বেশ মাথাব্যথার কারণ হয়ে দাঁড়ায়। যেমন, ধরা যাক নিচের ডকুমেন্টটা আমরা তৈরী করব -

<?xml version="1.0" encoding="UTF-8"?>
<blog>
    <author>
        <name>John Doe</name>
        <email>[email protected]</email>
    </author>
    <articles>
        <article published="true">
            <title>Article 1</title>
            <body>Article Body</body>
        </article>
        <article published="true">
            <title>Article 2</title>
            <body>Article Body</body>
        </article>
    </articles>
<blog>

minidom এর মাধ্যমে পাইথনে আমরা হয়তো এভাবে কোড লিখব -

from xml.dom import minidom

articles = [
    {
        'title': 'Article 1',
        'body': 'Article Body',
        'published': True
    },
    {
        'title': 'Article 2',
        'body': 'Article Body',
        'published': True
    }
]

root = minidom.Document()

blogElem = root.createElement('blog')
authorElem = root.createElement('author')

nameElem = root.createElement('name')
nameElem.appendChild(root.createTextNode('John Doe'))
authorElem.appendChild(nameElem)

emailElem = root.createElement('email')
emailElem.appendChild(root.createTextNode('[email protected]'))
authorElem.appendChild(emailElem)

articlesElem = root.createElement('articles')

for article in articles:
    articleElem = root.createElement('article')
    articleElem.setAttribute('published', 'true' if article['published'] else 'false')

    titleElem = root.createElement('title')
    titleElem.appendChild(root.createTextNode(article['title']))
    articleElem.appendChild(titleElem)

    bodyElem = root.createElement('body')
    bodyElem.appendChild(root.createTextNode(article['body']))
    articleElem.appendChild(bodyElem)

    articlesElem.appendChild(articleElem)

blogElem.appendChild(authorElem)
blogElem.appendChild(articlesElem)

root.appendChild(blogElem)
print(root.toprettyxml())

উপরের কোডটি ঠিকঠাক কাজ করে। কিন্তু XML ট্যাগগুলোর মধ্যে হায়ারার্কিক্যাল সম্পর্ক বোঝা কঠিন, মানে কোন ট্যাগ কার প্যারেন্ট বা চাইল্ড। কোডটা খুবই প্রসিডিউরিয়াল। এধরণের কোড মেনটেইন করা একটু ঝামেলা হতে পারে, কারণ ট্র্যাক করতে হবে কোন ট্যাগ কোথায় ইত্যাদি।

আমরা এখানে আরো ভালো করতে পারি। রিয়েক্ট বা হাইপারস্ক্রিপ্ট থেকে আমরা আইডিয়া ধার করতে পারি এবং এই কমপ্লেক্সিটিকে সহজভাবে সমাধান করতে পারি।

def element_builder(xml_doc):
    """
    Given a document returns a function to build xml elements.
    Args:
        xml_doc (xml.dom.minidom.Document)
    Returns:
        element (func)
    """

    def element(name, children, attributes=None):
        """
        An utility to help build xml tree in a managable way.
        Args:
            name (str) - tag name
            children (str|list|xml.dom.minidom.Element)
            attributes (dict)
        Returns:
            elem (xml.dom.minidom.Element)
        """

        elem = xml_doc.createElement(name)

        # set attributes if exists
        if attributes is not None and isinstance(attributes, dict):
            [elem.setAttribute(key, val) for key, val in attributes.items()]

        # set children if exists
        if children is not None:
            if isinstance(children, list) or isinstance(children, tuple):
                [elem.appendChild(c) for c in children]
            elif isinstance(children, str):
                elem.appendChild(xml_doc.createTextNode(children))
            else:
                elem.appendChild(children)

        return elem

    return element

এই হেলপার ফাংশনটি একটি XML ডকুমেন্ট নেয় এবং আরেকটি ফাংশন রিটার্ন করে। ওই ফাংশন দিয়ে আমরা নতুন এলিমেন্ট তৈরী করা, তাদের এট্রিবিউট সেট করা ও চাইল্ড এলিমেন্ট সংযুক্ত করা - এসব কাজ করতে পারি। এই সমস্ত কাজগুলো একটি মাত্র ফাংশন কল করে করা সম্ভব।

এই হেলপার ফাংশন ব্যবহার করলে আমাদের কোড হবে নিচের মতো -

from xml.dom import minidom
from .utility import element_builder


articles = [
    {
        'title': 'Article 1',
        'body': 'Article Body',
        'published': True
    },
    {
        'title': 'Article 2',
        'body': 'Article Body',
        'published': True
    }
]

root = minidom.Document()
el = element_builder(root)

elem = el('blog', [
    el('author', [
        el('name', 'John Doe'),
        el('email', '[email protected]')
    ]),
    el('articles', [
        el('article', [
            el('title', article['title']),
            el('body', article['body']),
        ], {'published': 'true' if article['published'] else 'false'})
        for article in articles
    ])
])

root.appendChild(elem)
print(root.toprettyxml())

এবার এই দুটো পদ্ধতির মধ্যে পার্থক্যটা দেখুন। আমার মতে এটা খুব ভালো পদ্ধতি কারণ ট্যাগগুলোর মধ্যে হায়ারার্কিক্যাল সম্পর্ক খুব সহজেই বোঝা যাচ্ছে। কোডের পরিমান অনেক কমে গিয়েছে। অথচ দুটো কোড একই XML ডকুমেন্ট তৈরী করে।

আমি আশা করি এটা হয়তো কারো কাজে আসবে।

programmingpythonetreetips & tricks