首页 > 解决方案 > jinja + 表单 + unicode 控制字符 + xml/docx 集成

问题描述

我正在根据用户在表单中输入的内容创建 Word 文档。但是,当用户输入一个 unicode 控制字符,并尝试使用 python-docx 包从中创建一个 word 文件时,会发生此错误:

File "src\lxml\apihelpers.pxi", line 1439, in lxml.etree._utf8
ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters

我设法通过在每个请求之前检查表单中的无效 xml 字符来解决这个问题(我有许多可能会出现此问题的表单),并从字段中删除任何无效的 xml 字符。然后我制作了一个新的不可变多字典,并用清理后的文本填充它。

from docx import Document
from docx.shared import Inches
from flask import Flask, render_template_string, request
from werkzeug.datastructures import ImmutableMultiDict

def valid_xml_char_ordinal(c):
    codepoint = ord(c)
    return (0x20 <= codepoint <= 0xD7FF or codepoint in (0x9, 0xA, 0xD) or
            0xE000 <= codepoint <= 0xFFFD or 0x10000 <= codepoint <= 0x10FFFF)

app = Flask(__name__)

@app.before_request
def before_request():
    if 'check_form_xml_validity' in request.form:
        tuple_list = []
        for field_name in request.form:
            all_field_values = request.form.getlist(field_name)
            for field_value in all_field_values:
                cleaned_field_value = ''.join(c for c in field_value if valid_xml_char_ordinal(c))
                tuple_list.append((field_name, cleaned_field_value))
        request.form = ImmutableMultiDict(tuple_list)

@app.route('/', methods=['GET', 'POST'])
def form_test():
    if request.method == 'GET':
        x = '' # this seemingly empty string is not empty, but contains a bunch of control characters
        return render_template_string(
            """<form action="{{ url_for('form_test') }}" method="post">
                <input name="some_field" value="{{x}}"><br>
                check the xml validity of this form? <br>
                <input type="checkbox" checked name="check_form_xml_validity"><br>
                <button>submit</button>
            </form>""",
            x=x)
    else:
        doc = Document()
        p = doc.add_paragraph(request.form['some_field'])
        return 'yay'

而且这种方法效果很好。但是,我似乎不太可能是唯一遇到此问题的人,但我找不到任何干净的解决方案。所以问题是,我真的应该以目前的方式解决这个问题吗?这很乏味,感觉就像我忽略了一些可以解决这个问题的 Flask 或 python-docx 设置或参数。

该示例功能齐全,如果选中复选框,before_request则执行该功能。如果未选中该复选框,则不会执行该复选框,并且将显示提到的服务器错误。

在此处输入图像描述

控制字符为:U+000C : <control-000C> (FORM FEED [FF])

标签: pythonflaskjinja2lxmlpython-docx

解决方案


unicode 中有大量的控制字符。因此,基本上,您需要删除控制字符,这是 unicode 字符中的类别之一。为此,我建议您使用unicodedata模块中的unicodedata.category 。

请参见下面的代码:

import unicodedata


def remove_control_chars(s):
    return "".join(ch for ch in s if unicodedata.category(ch)[0] != "C")

推荐阅读