xml.dom.minidom --- پیاده‌سازی کمینه‌ی DOM

کد منبع: Lib/xml/dom/minidom.py


xml.dom.minidom یک پیاده‌سازی حداقلی از رابط مدل شیء سند (DOM) با API مشابه زبان‌های دیگر است. این پیاده‌سازی به‌گونه‌ای در نظر گرفته شده است که ساده‌تر از DOM کامل و همچنین به‌طور قابل‌توجهی کوچک‌تر باشد. کاربرانی که از قبل با DOM مهارت ندارند، بهتر است به‌جای آن برای پردازش XML خود از ماژول xml.etree.ElementTree استفاده کنند.

توجه

اگر نیاز دارید داده‌های غیرقابل‌اعتماد یا احراز هویت‌نشده را تجزیه کنید، به امنیت XML مراجعه کنید.

برنامه‌های DOM معمولاً با تجزیه‌ی مقداری XML به یک DOM آغاز می‌شوند. با xml.dom.minidom، این کار از طریق توابع parse انجام می‌شود:

from xml.dom.minidom import parse, parseString

dom1 = parse('c:\\temp\\mydata.xml')  # parse an XML file by name

datasource = open('c:\\temp\\mydata.xml')
dom2 = parse(datasource)  # parse an open file

dom3 = parseString('<myxml>Some data<empty/> some more data</myxml>')

تابع parse() می‌تواند یک نام پرونده یا یک شیء پرونده باز را دریافت کند.

xml.dom.minidom.parse(filename_or_file, parser=None, bufsize=None)

یک Document از ورودی داده‌شده برمی‌گرداند. filename_or_file می‌تواند یک نام پرونده یا یک شیء شبه‌پرونده باشد. parser، در صورت داده شدن، باید یک شیء پارسر SAX2 باشد. این تابع هندلر سند (document handler) پارسر را تغییر می‌دهد و پشتیبانی از فضای نام را فعال می‌کند؛ سایر پیکربندی‌های پارسر (مانند تنظیم حل‌کننده موجودیت (entity resolver)) باید از قبل انجام شده باشند.

اگر XML را در یک رشته دارید، می‌توانید به‌جای آن از تابع parseString() استفاده کنید:

xml.dom.minidom.parseString(string, parser=None)

یک Document برمی‌گرداند که رشته را نشان می‌دهد. این متد یک شیء io.StringIO برای رشته ایجاد می‌کند و آن را به parse() می‌فرستد.

هر دو تابع یک شیء Document برمی‌گردانند که نشان‌دهنده محتوای سند است.

کاری که توابع parse() و parseString() انجام می‌دهند، اتصال یک پارسر XML به یک «سازنده DOM» (DOM builder) است که می‌تواند رویدادهای تجزیه را از هر پارسر SAX بپذیرد و آن‌ها را به یک درخت DOM تبدیل کند. نام این توابع شاید گمراه‌کننده باشد، اما هنگام یادگیری رابط‌ها به‌آسانی قابل‌درک هستند. تجزیه سند پیش از بازگشت این توابع کامل می‌شود؛ صرفاً این توابع خود پیاده‌سازی پارسری ارائه نمی‌دهند.

همچنین می‌توانید با فراخوانی یک متد روی یک شیء «پیاده‌سازی DOM»، یک Document ایجاد کنید. می‌توانید این شیء را با فراخوانی تابع getDOMImplementation() در بسته xml.dom یا ماژول xml.dom.minidom دریافت کنید. هنگامی که یک Document داشتید، می‌توانید گره‌های فرزند را برای پر کردن DOM به آن اضافه کنید:

from xml.dom.minidom import getDOMImplementation

impl = getDOMImplementation()

newdoc = impl.createDocument(None, "some_tag", None)
top_element = newdoc.documentElement
text = newdoc.createTextNode('Some textual content.')
top_element.appendChild(text)

هنگامی که یک شیء سند DOM داشته باشید، می‌توانید از طریق ویژگی‌ها و متدهای آن به بخش‌های سند XML خود دسترسی پیدا کنید. این ویژگی‌ها در مشخصات DOM تعریف شده‌اند. ویژگی اصلی شیء سند، ویژگی documentElement است. این ویژگی، المان اصلی سند XML را در اختیار شما قرار می‌دهد: المانی که همه‌ی سایر المان‌ها را در بر دارد. در اینجا یک برنامه نمونه آمده است:

dom3 = parseString("<myxml>Some data</myxml>")
assert dom3.documentElement.tagName == "myxml"

هنگامی که کارتان با یک درخت DOM به پایان رسید، می‌توانید به‌اختیار متد unlink() را برای تشویق پاک‌سازی زودهنگام اشیایی که دیگر مورد نیاز نیستند فراخوانی کنید. unlink() یک افزونه‌ی خاص xml.dom.minidom برای DOM API است که گره و نوادگان آن را عملاً بی‌استفاده می‌کند. در غیر این صورت، زباله‌روبی پایتون در نهایت اشیاء موجود در درخت را آزاد خواهد کرد.

همچنین ملاحظه نمائید

مشخصات سطح ۱ مدل شیء سند (DOM)

توصیه‌نامه‌ی W3C برای DOM که توسط xml.dom.minidom پشتیبانی می‌شود.

اشیای DOM

تعریف API DOM برای پایتون به‌عنوان بخشی از مستندات ماژول xml.dom ارائه شده است. این بخش تفاوت‌های بین API و xml.dom.minidom را فهرست می‌کند.

ارجاع‌های داخلی درون DOM را قطع کنید تا در نسخه‌های پایتون بدون زباله‌روبی چرخه‌ای، زباله‌روبی شود. حتی زمانی که زباله‌روبی چرخه‌ای در دسترس است، استفاده از این می‌تواند مقادیر زیادی حافظه را زودتر در دسترس قرار دهد، بنابراین فراخوانی این روی اشیای DOM به محض اینکه دیگر نیازی به آن‌ها نیست، روش خوبی است. فقط لازم است این روی شیء Document فراخوانی شود، اما می‌توان آن را روی گره‌های فرزند نیز فراخوانی کرد تا فرزندان آن گره حذف شوند.

می‌توانید با استفاده از دستور with از فراخوانی صریح این متد اجتناب کنید. کد زیر هنگام خروج از بلوک with، به‌طور خودکار پیوند dom را قطع می‌کند:

with xml.dom.minidom.parse(datasource) as dom:
    ... # Work with dom.
Node.writexml(writer, indent='', addindent='', newl='', encoding=None, standalone=None)

XML را در شیء نویسنده بنویسید. نویسنده ورودی را به‌صورت متن دریافت می‌کند، نه بایت؛ باید متد write() داشته باشد که با متد موجود در رابط شیء پرونده مطابقت دارد. پارامتر indent تورفتگی گره جاری است. پارامتر addindent تورفتگی افزایشی است که برای زیرگره‌های گره جاری استفاده می‌شود. پارامتر newl رشته‌ای را مشخص می‌کند که برای پایان دادن به سطرهای جدید استفاده می‌شود.

برای گره Document، می‌توان از یک آرگومان کلیدواژه‌ای اضافی به نام encoding برای مشخص کردن فیلد کدگذاری سرآیند XML استفاده کرد.

به‌طور مشابه، ذکر صریح آرگومان standalone باعث می‌شود اعلامیه‌های مستقل سند به پیش‌درآمد سند XML افزوده شوند. اگر مقدار به True تنظیم شود، standalone="yes" افزوده می‌شود، در غیر این صورت مقدار به "no" تنظیم می‌شود. ذکر نکردن آرگومان، اعلامیه را از سند حذف می‌کند.

تغییر یافته در نسخه‌ی 3.8: متد writexml() اکنون ترتیب صفت‌هایی را که کاربر تعیین کرده است حفظ می‌کند.

تغییر یافته در نسخه‌ی 3.9: پارامتر standalone افزوده شد.

Node.toxml(encoding=None, standalone=None)

یک رشته یا رشته بایتی حاوی XML بازنمایی‌شده توسط گره DOM را برمی‌گرداند.

با یک آرگومان encoding [1] صریح، نتیجه یک رشته بایت با کدگذاری مشخص است. بدون آرگومان encoding، نتیجه یک رشته یونیکد است و اعلامیه XML در رشته حاصل، کدگذاری را مشخص نمی‌کند. کدگذاری این رشته با کدگذاری غیر از UTF-8 احتمالاً نادرست است، زیرا UTF-8 کدگذاری پیش‌فرض XML است.

آرگومان standalone دقیقاً مانند writexml() رفتار می‌کند.

تغییر یافته در نسخه‌ی 3.8: متد toxml() اکنون ترتیب ویژگی‌ها را همان‌طور که کاربر تعیین کرده است حفظ می‌کند.

تغییر یافته در نسخه‌ی 3.9: پارامتر standalone افزوده شد.

Node.toprettyxml(indent='\t', newl='\n', encoding=None, standalone=None)

یک نسخه‌ی زیبانویسی‌شده از سند برمی‌گرداند. indent رشته‌ی تورفتگی را مشخص می‌کند و پیش‌فرض آن یک نویسه‌ی تب است؛ newl رشته‌ای را که در پایان هر خط درج می‌شود مشخص می‌کند و پیش‌فرض آن \n است.

آرگومان encoding مانند آرگومان متناظر در toxml() رفتار می‌کند.

آرگومان standalone دقیقاً مانند writexml() رفتار می‌کند.

تغییر یافته در نسخه‌ی 3.8: متد toprettyxml() اکنون ترتیب ویژگی‌های تعیین‌شده توسط کاربر را حفظ می‌کند.

تغییر یافته در نسخه‌ی 3.9: پارامتر standalone افزوده شد.

مثال DOM

این برنامه نمونه، مثال نسبتاً واقع‌گرایانه‌ای از یک برنامه ساده است. در این مورد خاص، از انعطاف‌پذیری DOM استفاده چندانی نمی‌کنیم.

import xml.dom.minidom

document = """\
<slideshow>
<title>Demo slideshow</title>
<slide><title>Slide title</title>
<point>This is a demo</point>
<point>Of a program for processing slides</point>
</slide>

<slide><title>Another demo slide</title>
<point>It is important</point>
<point>To have more than</point>
<point>one slide</point>
</slide>
</slideshow>
"""

dom = xml.dom.minidom.parseString(document)

def getText(nodelist):
    rc = []
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc.append(node.data)
    return ''.join(rc)

def handleSlideshow(slideshow):
    print("<html>")
    handleSlideshowTitle(slideshow.getElementsByTagName("title")[0])
    slides = slideshow.getElementsByTagName("slide")
    handleToc(slides)
    handleSlides(slides)
    print("</html>")

def handleSlides(slides):
    for slide in slides:
        handleSlide(slide)

def handleSlide(slide):
    handleSlideTitle(slide.getElementsByTagName("title")[0])
    handlePoints(slide.getElementsByTagName("point"))

def handleSlideshowTitle(title):
    print(f"<title>{getText(title.childNodes)}</title>")

def handleSlideTitle(title):
    print(f"<h2>{getText(title.childNodes)}</h2>")

def handlePoints(points):
    print("<ul>")
    for point in points:
        handlePoint(point)
    print("</ul>")

def handlePoint(point):
    print(f"<li>{getText(point.childNodes)}</li>")

def handleToc(slides):
    for slide in slides:
        title = slide.getElementsByTagName("title")[0]
        print(f"<p>{getText(title.childNodes)}</p>")

handleSlideshow(dom)

minidom و استاندارد DOM

ماژول xml.dom.minidom اساساً یک DOM سازگار با DOM 1.0 است که برخی ویژگی‌های DOM 2 (عمدتاً ویژگی‌های فضای نام) را دارد.

استفاده از رابط DOM در پایتون ساده است. قواعد نگاشت زیر اعمال می‌شوند:

  • رابط‌ها از طریق اشیای نمونه قابل دسترسی هستند. برنامه‌ها نباید خودشان کلاس‌ها را نمونه‌سازی کنند؛ باید از توابع سازنده موجود در شیء Document استفاده کنند. رابط‌های مشتق‌شده از تمام عملیات (و ویژگی‌ها)ی رابط‌های پایه، به‌همراه هر عملیات جدیدی پشتیبانی می‌کنند.

  • از عملیات‌ها به‌عنوان متد استفاده می‌شود. از آن‌جا که DOM فقط از پارامترهای in استفاده می‌کند، آرگومان‌ها با ترتیب معمول (از چپ به راست) ارسال می‌شوند. هیچ آرگومان اختیاری وجود ندارد. عملیات‌های void، None را برمی‌گردانند.

  • ویژگی‌های IDL به ویژگی‌های نمونه نگاشت می‌شوند. برای سازگاری با نگاشت زبان OMG IDL برای پایتون، می‌توان به ویژگی foo از طریق متدهای دسترسی‌دهنده _get_foo() و _set_foo() نیز دسترسی داشت. ویژگی‌های readonly نباید تغییر داده شوند؛ این موضوع در ران‌تایم اعمال نمی‌شود.

  • انواع short int، unsigned int، unsigned long long و boolean همگی به اشیاء عدد صحیح پایتون نگاشت می‌شوند.

  • نوع DOMString به رشته‌های پایتون نگاشت می‌شود. xml.dom.minidom از بایت‌ها یا رشته‌ها پشتیبانی می‌کند، اما به‌طور معمول رشته‌ها را تولید می‌کند. مقادیر از نوع DOMString ممکن است در مواردی که مشخصه DOM از W3C اجازه داشتن مقدار null در IDL را بدهد، None نیز باشند.

  • اعلان‌های const به متغیرهایی در محدوده‌ی مربوط به خود نگاشت می‌شوند (برای مثال xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE)؛ این متغیرها نباید تغییر کنند.

  • DOMException در حال حاضر در xml.dom.minidom پشتیبانی نمی‌شود. در عوض، xml.dom.minidom از استثناهای استاندارد پایتون مانند TypeError و AttributeError استفاده می‌کند.

  • Each of the NodeList and NamedNodeMap interfaces has two implementations, which provide additional methods and operations.

    childNodes is a subclass of list, or, for nodes which cannot have children, a subclass of tuple. It supports iteration, concatenation, indexing and slicing.

    attributes supports len(), the in operator, subscription by a name or by a (namespaceURI, localName) tuple, assignment and deletion, and the methods get(), keys(), keysNS(), values(), items() and itemsNS(). entities and notations are read-only and support only len() and subscription by a name.

  • strictErrorChecking and specified are always False.

  • removeAttribute() and removeAttributeNS() raise NotFoundErr if there is no matching attribute, while the DOM specifies that this has no effect.

رابط‌های زیر در xml.dom.minidom پیاده‌سازی ندارند:

  • DOMTimeStamp

  • EntityReference

بیشتر این‌ها اطلاعاتی در سند XML را بازتاب می‌دهند که برای بیشتر کاربران DOM کاربرد عمومی ندارد.

پانویس‌ها