如何管理用于格式化 XDocument 的 "NO-BREAK SPACE"?
How to manage "NO-BREAK SPACE" used for formatting of XDocument?
有些用户试图上传带有 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
可以包含那些奇怪的空格),因此纯文本处理(即使使用聪明的正则表达式)也不是选项。
XML包含U+00A0 NO-BREAK SPACE
characters is perfectly well-formed. Your problem is that, according to the Extensible Markup Language (XML) 1.0 (Fourth Edition),这是XmlReader
支持的XML标准,U+00A0
是不算白space 字符:
White Space
[3]S ::= (#x20 | #x9 | #xD | #xA)+
(这与 Unicode consortium 对白色 space 的定义形成对比,后者确实包括 U+00A0
。)
因此,当 <myConfig>
被加载时,它被解释为具有 混合内容 包括文本而不是无关紧要的白色 space,这反过来会导致错误在根据您的架构(未显示)验证 <myConfig>
时抛出,因为该元素可能不允许架构具有文本值。
防止错误的一种方法是创建一个自定义 XmlReader
将 U+00A0
翻译成,比如说,一个常规的 space 字符:
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);
}
但请注意 XmlTextReader
根据其 docs 已弃用:
Starting with the .NET Framework 2.0, we recommend that you create XmlReader instances by using the XmlReader.Create method to take advantage of new functionality.
因此您可能想要创建一个 XmlReader
decorator as shown here or here(在 Chaining XmlReaders 下)然后将装饰器子类化并在其中修复文本值。虽然需要做更多的工作,但这种方法可能更可靠。
演示 fiddle here.
有些用户试图上传带有 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
可以包含那些奇怪的空格),因此纯文本处理(即使使用聪明的正则表达式)也不是选项。
XML包含U+00A0 NO-BREAK SPACE
characters is perfectly well-formed. Your problem is that, according to the Extensible Markup Language (XML) 1.0 (Fourth Edition),这是XmlReader
支持的XML标准,U+00A0
是不算白space 字符:
White Space
[3]
S ::= (#x20 | #x9 | #xD | #xA)+
(这与 Unicode consortium 对白色 space 的定义形成对比,后者确实包括 U+00A0
。)
因此,当 <myConfig>
被加载时,它被解释为具有 混合内容 包括文本而不是无关紧要的白色 space,这反过来会导致错误在根据您的架构(未显示)验证 <myConfig>
时抛出,因为该元素可能不允许架构具有文本值。
防止错误的一种方法是创建一个自定义 XmlReader
将 U+00A0
翻译成,比如说,一个常规的 space 字符:
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);
}
但请注意 XmlTextReader
根据其 docs 已弃用:
Starting with the .NET Framework 2.0, we recommend that you create XmlReader instances by using the XmlReader.Create method to take advantage of new functionality.
因此您可能想要创建一个 XmlReader
decorator as shown here or here(在 Chaining XmlReaders 下)然后将装饰器子类化并在其中修复文本值。虽然需要做更多的工作,但这种方法可能更可靠。
演示 fiddle here.