تحرير XML كقاموس في بيثون؟
-
02-07-2019 - |
سؤال
أحاول إنشاء ملفات xml مخصصة من ملف xml قالب في بيثون.
من الناحية النظرية، أريد القراءة في قالب XML، وإزالة بعض العناصر، وتغيير بعض سمات النص، وكتابة ملف XML الجديد في ملف.أردت أن يعمل شيء من هذا القبيل:
conf_base = ConvertXmlToDict('config-template.xml')
conf_base_dict = conf_base.UnWrap()
del conf_base_dict['root-name']['level1-name']['leaf1']
del conf_base_dict['root-name']['level1-name']['leaf2']
conf_new = ConvertDictToXml(conf_base_dict)
الآن أريد أن أكتب إلى ملف ، لكنني لا أرى كيفية الوصول إلى ElementTree.ElementTree.write ()
conf_new.write('config-new.xml')
هل هناك طريقة ما للقيام بذلك، أو هل يمكن لأي شخص أن يقترح القيام بذلك بطريقة مختلفة؟
المحلول
لسهولة التعامل مع XML في لغة بايثون، يعجبني ملف حساء جميل مكتبة.يعمل شيء من هذا القبيل:
نموذج ملف XML:
<root>
<level1>leaf1</level1>
<level2>leaf2</level2>
</root>
رمز بايثون:
from BeautifulSoup import BeautifulStoneSoup, Tag, NavigableString
soup = BeautifulStoneSoup('config-template.xml') # get the parser for the xml file
soup.contents[0].name
# u'root'
يمكنك استخدام أسماء العقد كطرق:
soup.root.contents[0].name
# u'level1'
من الممكن أيضًا استخدام regexes:
import re
tags_starting_with_level = soup.findAll(re.compile('^level'))
for tag in tags_starting_with_level: print tag.name
# level1
# level2
تعد إضافة العقد الجديدة وإدراجها أمرًا بسيطًا جدًا:
# build and insert a new level with a new leaf
level3 = Tag(soup, 'level3')
level3.insert(0, NavigableString('leaf3')
soup.root.insert(2, level3)
print soup.prettify()
# <root>
# <level1>
# leaf1
# </level1>
# <level2>
# leaf2
# </level2>
# <level3>
# leaf3
# </level3>
# </root>
نصائح أخرى
سيؤدي هذا إلى الحصول على إملاء ناقص السمات ...لا أعرف إذا كان هذا مفيدًا لأي شخص.كنت أبحث عن ملف XML لإملاء الحل بنفسي عندما توصلت إلى هذا.
import xml.etree.ElementTree as etree
tree = etree.parse('test.xml')
root = tree.getroot()
def xml_to_dict(el):
d={}
if el.text:
d[el.tag] = el.text
else:
d[el.tag] = {}
children = el.getchildren()
if children:
d[el.tag] = map(xml_to_dict, children)
return d
هذا: http://www.w3schools.com/XML/note.xml
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
سوف يساوي هذا:
{'note': [{'to': 'Tove'},
{'from': 'Jani'},
{'heading': 'Reminder'},
{'body': "Don't forget me this weekend!"}]}
لست متأكدًا مما إذا كان تحويل المعلومات المعينة إلى الإملاءات المتداخلة أولاً أسهل.باستخدام ElementTree، يمكنك القيام بذلك:
import xml.etree.ElementTree as ET
doc = ET.parse("template.xml")
lvl1 = doc.findall("level1-name")[0]
lvl1.remove(lvl1.find("leaf1")
lvl1.remove(lvl1.find("leaf2")
# or use del lvl1[idx]
doc.write("config-new.xml")
تم تصميم ElementTree بحيث لا تضطر إلى تحويل أشجار XML الخاصة بك إلى قوائم وسمات أولاً، حيث إنها تستخدم ذلك بالضبط داخليًا.
كما أنها تدعم مجموعة فرعية صغيرة من XPath.
تعديلي لإجابة دانيال لإعطاء قاموس أكثر إتقانًا بشكل هامشي:
def xml_to_dictionary(element):
l = len(namespace)
dictionary={}
tag = element.tag[l:]
if element.text:
if (element.text == ' '):
dictionary[tag] = {}
else:
dictionary[tag] = element.text
children = element.getchildren()
if children:
subdictionary = {}
for child in children:
for k,v in xml_to_dictionary(child).items():
if k in subdictionary:
if ( isinstance(subdictionary[k], list)):
subdictionary[k].append(v)
else:
subdictionary[k] = [subdictionary[k], v]
else:
subdictionary[k] = v
if (dictionary[tag] == {}):
dictionary[tag] = subdictionary
else:
dictionary[tag] = [dictionary[tag], subdictionary]
if element.attrib:
attribs = {}
for k,v in element.attrib.items():
attribs[k] = v
if (dictionary[tag] == {}):
dictionary[tag] = attribs
else:
dictionary[tag] = [dictionary[tag], attribs]
return dictionary
مساحة الاسم هي سلسلة xmlns، بما في ذلك الأقواس، التي تلحقها ElementTree بجميع العلامات، لذا قمت هنا بمسحها حيث توجد مساحة اسم واحدة للمستند بأكمله
ملحوظة: لقد قمت بتعديل ملف xml الأولي أيضًا، بحيث تنتج العلامات "الفارغة" على الأكثر خاصية نصية ' ' في تمثيل ElementTree
spacepattern = re.compile(r'\s+')
mydictionary = xml_to_dictionary(ElementTree.XML(spacepattern.sub(' ', content)))
سيعطي على سبيل المثال
{'note': {'to': 'Tove',
'from': 'Jani',
'heading': 'Reminder',
'body': "Don't forget me this weekend!"}}
إنه مصمم لملفات XML معينة تعادل بشكل أساسي ملف json، ويجب أن يتعامل مع سمات العناصر مثل
<elementName attributeName='attributeContent'>elementContent</elementName>
أيضاً
هناك إمكانية دمج قاموس السمات / قاموس العلامات الفرعية بشكل مشابه لكيفية دمج العلامات الفرعية المتكررة، على الرغم من أن تداخل القوائم يبدو مناسبًا نوعًا ما :-)
إضافة هذا السطر
d.update(('@' + k, v) for k, v in el.attrib.iteritems())
في ال رمز المستخدم 247686 يمكن أن يكون لديك سمات العقدة أيضًا.
وجدت ذلك في هذا المنصب https://stackoverflow.com/a/7684581/1395962
مثال:
import xml.etree.ElementTree as etree
from urllib import urlopen
xml_file = "http://your_xml_url"
tree = etree.parse(urlopen(xml_file))
root = tree.getroot()
def xml_to_dict(el):
d={}
if el.text:
d[el.tag] = el.text
else:
d[el.tag] = {}
children = el.getchildren()
if children:
d[el.tag] = map(xml_to_dict, children)
d.update(('@' + k, v) for k, v in el.attrib.iteritems())
return d
اتصل باسم
xml_to_dict(root)
هل جربت هذا؟
print xml.etree.ElementTree.tostring( conf_new )
الطريق المباشر بالنسبة لي:
root = ET.parse(xh)
data = root.getroot()
xdic = {}
if data > None:
for part in data.getchildren():
xdic[part.tag] = part.text
يحتوي XML على مجموعة معلومات غنية، ويتطلب الأمر بعض الحيل الخاصة لتمثيل ذلك في قاموس Python.يتم ترتيب العناصر، وتمييز السمات عن أجسام العناصر، وما إلى ذلك.
أحد المشاريع للتعامل مع الرحلات ذهابًا وإيابًا بين قواميس XML وPython، مع بعض خيارات التكوين للتعامل مع المفاضلات بطرق مختلفة هو دعم XML في أدوات التخليل.مطلوب الإصدار 1.3 وأحدث.إنها ليست Python خالصة (وفي الواقع تم تصميمها لتسهيل التفاعل بين C++ / Python)، ولكنها قد تكون مناسبة لحالات استخدام مختلفة.