如何管理用于XDocument格式化的“ NO-BREAK SPACE”?

时间:2019-12-30 14:25:19

标签: c# .net xml utf-8 xsd

某些用户正试图上传带有U+00A0 NO-BREAK SPACE符号用于格式设置的文档:

<myConfig>
  <defaultConfig>
    <defaultTitle>Hello!</defaultTitle>
  </defaultConfig>
</myConfig>

我了解根据规范,它可能是无效的XML,但是如果复制了此数据的某些不良编辑器或网页给出了这些讨厌的文字,则我需要予以支持或打印出错误消息。

我正在做

XDocument d = XDocument.Parse(xmlFromUser);
XmlTextReader xmlReader = new XmlTextReader(xsdSchemaText, XmlNodeType.Document, null);
XmlSchema xmlSchema = XmlSchema.Read(xmlReader, null);
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(xmlSchema);              
d.Validate(schemas, (sender, eventArgs) =>
{
    // process errors here
    //Console.WriteLine($"[{eventArgs.Severity}] {eventArgs.Message}");
});

这给了我很多这样的错误:

[Error] The element 'myConfig' cannot contain text. List of possible elements expected: 'defaultConfig'.

对于实际输入,它会为每个带有NO-BREAK SPACE的块产生一个错误,这使用户认为系统已损坏。普通用户无法检测和解决文档中的此类问题。

这就是为什么我需要忽略这些字符,将其转换为普通空格或执行任何其他使上述XML有效的操作的原因。但这是一个很大的系统,我不想影响现有值中的任何内容(例如,defaultTitle可以包含那些怪异的空格是可以的),因此纯文本处理(即使使用聪明的Regexes)也不是选项。

1 个答案:

答案 0 :(得分:1)

包含U+00A0 NO-BREAK SPACE个字符的

XML格式正确。您的问题是,根据XmlReader支持的XML标准Extensible Markup Language (XML) 1.0 (Fourth Edition)U+00A0视为空白字符

空白

[3] S ::= (#x20 | #x9 | #xD | #xA)+

(这与包含U+00A0的{​​{3}}对空格的定义相反。)

因此,在加载<myConfig>时,它被解释为具有混合内容,其中包含除无关紧要的空格之外的其他文本,从而导致在验证<myConfig>时引发错误与您的架构(未显示)相对应,因为该架构可能不允许该元素具有文本值。

一种防止错误的方法是创建一个自定义XmlReader,将U+00A0转换为常规的空格字符:

public class XmlNoBreakSpaceTextReader : XmlTextReader
{
    public XmlNoBreakSpaceTextReader(TextReader reader) : base(reader) { }

    string overrideValue = null;
    XmlNodeType? overrideType = null;

    public override string Value { get { return overrideValue ?? base.Value; } }

    public override XmlNodeType NodeType { get { return overrideType ?? base.NodeType; } }

    public override bool Read()
    {
        overrideValue = null;
        overrideType = null;
        while (base.Read())
        {
            var nodeType = base.NodeType;
            if (nodeType == XmlNodeType.Text)
            {
                var value = base.Value;
                // Maybe check here that string.IsNullOrWhiteSpace(value) and only replace nonbreaking spaces in whitespace strings?
                var newValue = value.Replace('\u00A0', ' ');
                if ((object)newValue != (object)value)
                {
                    var newNodeType = newValue.All(c => XmlConvert.IsWhitespaceChar(c)) ? XmlNodeType.Whitespace : nodeType;
                    if (newNodeType == XmlNodeType.Whitespace && WhitespaceHandling != WhitespaceHandling.All)
                        continue;
                    overrideValue = newValue;
                    overrideType = newNodeType;
                    return true;
                }
            }
            return true;
        }
        return false;
    }
}

然后按以下方式使用它:

XDocument d;
using (var textReader = new StringReader(xmlFromUser))
using (var reader = new XmlNoBreakSpaceTextReader(textReader))
{
    d = XDocument.Load(reader);
}

请注意,根据其Unicode consortium,不推荐使用XmlTextReader

从.NET Framework 2.0开始,我们建议您使用docs方法来创建XmlReader实例,以利用新功能。

因此,您可能想要创建XmlReader.Createdecorator(在 Chaining XmlReaders 下)XmlReader here,然后将装饰器和在此处修复文本值。虽然需要更多工作,但是这种方法可能更健壮。

演示小提琴here