1.项目背景
因为工作需要,xml文件夹下需要修改xml节点属性,统一定制poll_time属性结点,添加的xml结点目录为board\item\cpu\devs\dev\list\port\defaulttype下面添加属性attribute\poll_time 下interval =1000
手动添加太过于烦琐,尤其是有些单板存在很多端口的情况,考虑用python脚本添加。项目实例如下:
<?xml version="1.0" encoding="utf-8"?>
<board boardid ="2377" name="FOAD20" service="boardctrl">
<item phybomid="0xff" customid="0xff" pcb="0xff">
<cpu type="_CPU_ARMQORIQLE" index="0" logicindex="0" cpucardindex="0xff" cpucardtype="DEV_TYPE_BOARD_SELF" >
<devs>
<dev type="DEV_TYPE_PORT" name="osc_voa_oba_opa_soms" >
<list index="0" service="ethsubcard">
<attribute>
<firmware_version val="driver"/>
</attribute>
<port range="[1,1]" >
<defaulttype value="PORT_TYPE_OSC_SI" >
<interrupts>
<interrupt event="289" period="2" limit="50" enable="yes"/>
</interrupts>
</defaulttype>
</port>
</list>
<list index="1" service="ethsubcard">
<port range="[1,1]" >
<defaulttype value="PORT_TYPE_OSC_SO" >
<interrupts>
<interrupt event="289" period="2" limit="50" enable="yes"/>
</interrupts>
</defaulttype>
</port>
</list>
<list index="2" service="ethsubcard">
<port range="[1,1]" >
<defaulttype value="PORT_TYPE_MONITOR_IN" >
<interrupts>
<interrupt event="289" period="2" limit="50" enable="yes"/>
</interrupts>
</defaulttype>
</port>
</list>
<list index="3" service="ethsubcard">
<port range="[1,1]" >
<defaulttype value="PORT_TYPE_VOA_MODULE" >
<interrupts>
<interrupt event="289" period="2" limit="50" enable="yes"/>
</interrupts>
</defaulttype>
</port>
</list>
<list index="4" service="ethsubcard">
<port range="[1,1]" >
<defaulttype value="PORT_TYPE_OTS_TTP_SI" >
<interrupts>
<interrupt event="289" period="2" limit="50" enable="yes"/>
</interrupts>
</defaulttype>
</port>
</list>
2.开发过程中遇到的困难
1.python解析的xml结点读出来后,每一个xml节点按照字典序排序,极大的改变了xml的节点结构,不利于代码走查和代码管理
2.python开发过程中发现xml结点的编码方式 <?xml version="1.0" encoding="utf-8" ?> 为双引号而并非单引号
3.使用python脚本最大的误区是使用了python3.7版本导致版本不对,前面的xml按照顺序写入编译找不到相关的库,而因为不熟悉,想了各种方式没有解决
解决方法:
基于python2.7重写python的element.py源码,如下所示:
# =======================================================================
# 重写python源码,禁止读取xml后顺序写入
# =======================================================================
def _serialize_xml(write, elem, encoding, qnames, namespaces):
tag = elem.tag
text = elem.text
if tag is ET.Comment:
write("<!--%s-->" % ET._encode(text, encoding))
elif tag is ET.ProcessingInstruction:
write("<?%s?>" % ET._encode(text, encoding))
else:
tag = qnames[tag]
if tag is None:
if text:
write(ET._escape_cdata(text, encoding))
for e in elem:
_serialize_xml(write, e, encoding, qnames, None)
else:
write("<" + tag)
items = elem.items()
if items or namespaces:
if namespaces:
for v, k in sorted(namespaces.items(),
key=lambda x: x[1]): # sort on prefix
if k:
k = ":" + k
write(" xmlns%s=\"%s\"" % (
k.encode(encoding),
ET._escape_attrib(v, encoding)
))
# for k, v in sorted(items): # lexical order
for k, v in items: # Monkey patch
if isinstance(k, ET.QName):
k = k.text
if isinstance(v, ET.QName):
v = qnames[v.text]
else:
v = ET._escape_attrib(v, encoding)
write(" %s=\"%s\"" % (qnames[k], v))
if text or len(elem):
write(">")
if text:
write(ET._escape_cdata(text, encoding))
for e in elem:
_serialize_xml(write, e, encoding, qnames, None)
write("</" + tag + ">")
else:
write("/>")
if elem.tail:
write(ET._escape_cdata(elem.tail, encoding))
ET._serialize_xml = _serialize_xml
def _serialize_text(write, elem, encoding):
for part in elem.itertext():
write(part.encode(encoding))
if elem.tail:
write(elem.tail.encode(encoding))
ET._serialize_text = _serialize_text
_serialize = {
"xml": _serialize_xml,
"html": ET._serialize_html,
"text": _serialize_text,
# this optional method is imported at the end of the module
# "c14n": _serialize_c14n,
}
ET._serialize = _serialize
def write(self, file_or_filename,
# keyword arguments
encoding=None,
xml_declaration=None,
default_namespace=None,
method=None):
# assert self._root is not None
if not method:
method = "xml"
elif method not in _serialize:
# FIXME: raise an ImportError for c14n if ElementC14N is missing?
raise ValueError("unknown method %r" % method)
if hasattr(file_or_filename, "write"):
file = file_or_filename
else:
file = open(file_or_filename, "wb")
# file = codecs.open(file_or_filename,"w","utf-8-sig")
write = file.write
if not encoding:
if method == "c14n":
encoding = "utf-8"
else:
encoding = "us-ascii"
elif xml_declaration or (xml_declaration is None and
encoding not in ("utf-8", "us-ascii")):
if method == "xml":
write('<?xml version="1.0" encoding="%s"?>\r\n' % encoding)
if method == "text":
_serialize_text(write, self._root, encoding)
else:
qnames, namespaces = ET._namespaces(
self._root, encoding, default_namespace
)
serialize = _serialize[method]
serialize(write, self._root, encoding, qnames, namespaces)
if file_or_filename is not file:
file.close()
ET.ElementTree.write = write
class OrderedXMLTreeBuilder(ET.XMLTreeBuilder):
def _start_list(self, tag, attrib_in):
fixname = self._fixname
tag = fixname(tag)
attrib = OrderedDict()
if attrib_in:
for i in range(0, len(attrib_in), 2):
attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i + 1])
return self._target.start(tag, attrib)
# =======================================================================
# 重写python源码,禁止读取xml后顺序写入
# =======================================================================
4.在修改xml过程中,发现写入的xml节点不能可视化显示,可读性能差,只有用浏览器打开正常显示,显然这样不符合我们的要求,需要新增函数:
def indent(elem, level=0):
i = "\n" + level*"\t"
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + "\t"
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
5.在修改xml文件的时候,有的文件因为table转了空格,有的文件因为没有转空格,而代码走查过程中需要xml文件实现table转空格需求,否则会出现大量修改不对称和修改后大面积table显示问题
#table转空格
def tableSpace(file_name,lis_out,tab_num = 4):
file_str = open(file_name,"r").read()
if "\t" in file_str:
lis_out.append(file_name)
open(file_name,"w").write(file_str.expandtabs(tab_num))
6.python新增节点存在需求不明确的地方:
(1)attribute节点存在的情况,直接添加poll_time
(2)attribute节点不存在的情况,需要先添加attribute再添加
# 遍历所有的defaulttype标签
for defaulttype in root.findall("item/cpu/devs/dev/list/port/defaulttype"):
attribute = defaulttype.find("attribute")
if attribute is None:
attribute = ET.SubElement(defaulttype,"attribute")
subElement(attribute, "poll_time", "interval","1000")
else:
poll_time = attribute.find("poll_time")
if poll_time is None:#判断一下是不是空的,没有这个属性结点才写
subElement(attribute, "poll_time", "interval","1000")