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 افزوده شد.

تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): Namespace declarations missing for the serialized element and its attributes are now written. It now works for DocumentFragment nodes.

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 افزوده شد.

تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): It now works for DocumentFragment nodes.

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

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

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

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

No indentation is added inside an element which is marked with xml:space="preserve", which is declared in the DTD as not having element content, or, in absence of such declaration, which contains text, because this would change its content.

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

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

تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): Whitespace is no longer added inside an element with mixed content or marked with xml:space="preserve". It now works for DocumentFragment nodes.

مثال 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 استفاده می‌کند.

  • هر یک از رابط‌های NodeList و NamedNodeMap دو پیاده‌سازی دارند، که متدها و عملیات‌های اضافی فراهم می‌کنند.

    childNodes یک زیرکلاس از list است، یا برای گره‌هایی که نمی‌توانند فرزند داشته باشند، یک زیرکلاس از tuple است. این ویژگی پیمایش، الحاق، نمایه‌سازی و برش را پشتیبانی می‌کند.

    attributes از len()، عملگر in، زیرنویسی با یک نام یا با یک تاپل (namespaceURI, localName)، انتساب و حذف، و متدهای get()، keys()، keysNS()، values()، items() و itemsNS() پشتیبانی می‌کند. entities و notations فقط‌خواندنی هستند و تنها از len() و زیرنویسی با یک نام پشتیبانی می‌کنند.

  • strictErrorChecking is always False.

    تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): Previously, specified was always False.

  • The constraints of the DOM are now enforced, and the corresponding exceptions are raised.

    تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): Previously, many invalid operations silently succeeded and produced an invalid document, but removing an absent attribute raised NotFoundErr.

    تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): Namespaces are now validated in the factory methods and when setting prefix of an attribute.

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

  • DOMTimeStamp

This reflects information in the XML document that is not of general utility to most DOM users.

تغییر یافته در نسخه‌ی 3.16.0a0 (unreleased): EntityReference is now implemented. Note that the parser expands entity references, so they only occur in a document if created explicitly.

پانویس‌ها