У меня есть стороннее приложение, которое анализирует магические строки в файле XML, хотя оно должно обрабатывать их как символьные литералы. В качестве примера предположим, что мой XML содержит следующий сегмент:
<element>Sentence containing magicString</element>
Чтобы стороннее приложение не анализировало magicString
как команду, я хочу преобразовать этот фрагмент xml в:
<element>Sentence containing magicString</element>
Как я могу добиться этого в Python, не выполняя глобальный поиск-замену (например, могут быть элементы с именем magicString
, которые нельзя переименовать, или XML недействителен)? Следующее иллюстрирует то, что я пытался:
from xml.etree import ElementTree
xml = ElementTree.parse(xmlPath)
element = xml.find('.//grandparent/parent/element'):
element.text = 'magicString'
xml.write(xmlPath)
Проблема в том, что при присвоении свойства Element.text
экранируется текст, поэтому результатом является XML-файл со следующим содержимым:
<element>&#109;agicString</element>
🤔 А знаете ли вы, что...
С Python можно создавать ботов для социальных сетей и мессенджеров.
Мне понравилось решение Ицхака и Дэниела.
Возможно, вам тоже поможет хак с saxutils
ссылка:
import xml.etree.ElementTree as ET
from xml.sax.saxutils import escape
xml_ = """<element>Sentence containing magicString</element>"""
root = ET.fromstring(xml_)
print('Origin:', root.text)
a = escape(f"{root.text}", {"magicString": "magicString"})
print('Escaped:', a)
root.text = a
with open("w_xml", 'w') as f:
f.write(ET.tostring(root).decode().replace("&","&"))
# <element>Sentence containing magicString</element>
Выход:
Origin: Sentence containing magicString
Escaped: Sentence containing magicString
Вот решение на основе XSLT.
Он будет искать только текстовые узлы, содержащие «magicString». Элементы XML не затрагиваются.
Амперсанд всегда должен обозначаться как &
в XML. В противном случае XML был бы неправильно сформирован.
Вы можете скачать движок Saxon Python XSLT 3.0 здесь: Загрузки Python
Saxon Home Edition (HE) предоставляется бесплатно.
Входной XML
<root>
<magicString>Another sentence</magicString>
<element>Sentence containing magicString</element>
<city>Miami</city>
</root>
XSLT 3.0
<?xml version = "1.0"?>
<xsl:stylesheet version = "3.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" omit-xml-declaration = "yes"
encoding = "UTF-8" indent = "yes"/>
<xsl:strip-space elements = "*"/>
<xsl:param name = "findMe" select = "'magicString'"/>
<!--Identity transform-->
<xsl:mode on-no-match = "shallow-copy"/>
<xsl:template match = "text()[contains(., $findMe)]">
<xsl:value-of select = "replace(., $findMe, concat('&#109;', substring($findMe, 2, 100)))"/>
</xsl:template>
</xsl:stylesheet>
Выходной XML
<root>
<magicString>Another sentence</magicString>
<element>Sentence containing &#109;agicString</element>
<city>Miami</city>
</root>
Питон
from saxonche import *
output_file = 'output.xml'
with PySaxonProcessor(license=False) as proc:
print(proc.version)
try:
xsltproc = proc.new_xslt30_processor()
document = proc.parse_xml(xml_file_name='input.xml')
executable = xsltproc.compile_stylesheet(stylesheet_file = "process.xslt")
output = executable.transform_to_string(xdm_node=document)
with open(output_file, 'wb') as f:
f.write(output.encode('utf-8'))
except PySaxonApiError as err:
print('Error during function call', err)
Вот модифицированная версия XSLT, в которой используется другой символ (в данном случае ²
(надстрочный индекс 2)) и карта символов для сопоставления его с нужной ссылкой на объект. Вам просто нужно использовать символ, которого еще нет в вашем вводе, чтобы его случайно не заменили.
<xsl:stylesheet version = "3.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" expand-text = "yes">
<xsl:output indent = "yes" use-character-maps = "magic"/>
<xsl:strip-space elements = "*"/>
<xsl:character-map name = "magic">
<xsl:output-character character = "²" string = "&#109;"/>
</xsl:character-map>
<xsl:param name = "findMe" select = "'magicString'"/>
<xsl:mode on-no-match = "shallow-copy"/>
<xsl:template match = "text()[contains(., $findMe)]">
<xsl:value-of select = "replace(., $findMe, '²'||substring($findMe, 2))"/>
</xsl:template>
</xsl:stylesheet>
Это производит:
<root>
<magicString>Another sentence</magicString>
<element>Sentence containing magicString</element>
<city>Miami</city>
</root>
Снимок экрана Python/XSLT в PyCharm, дающий желаемый результат: