mah*_*eng 6 c# xml entity linq-to-xml
我编写了一个工具来修复一些XML文件(即插入一些缺少的属性/值),使用C#和Linq-to-XML.该工具将现有XML文件加载到XDocument对象中.然后,它通过节点向下解析以插入缺失的数据.之后,它调用XDocument.Save()将更改保存到另一个目录.
所有这一切都很好,除了一件事:任何 XML文件中文本中的实体将替换为换行符.当然,实体代表一个新行,但我需要在XML中保留实体,因为另一个消费者需要它.
有没有办法保存修改后的XDocument而不会丢失 实体?
谢谢.
Dou*_*las 11
这些

实体在技术上被称为XML中的"数字字符引用",并且在将原始文档加载到XML中时它们被解析XDocument
.这使得您的问题有待解决,因为XDocument
在加载之后无法区分已解析的空白实体与无关紧要的空白(通常用于为纯文本查看器格式化XML文档).因此,以下仅适用于您的文档没有任何无关紧要的空白.
该System.Xml
库允许通过将类的NewLineHandling
属性设置为来保留空白实体.然而,文本节点内,这只会entitize 来的,而不是来.XmlWriterSettings
Entitize
\r

\n


最简单的解决方案是从XmlWriter
类派生并覆盖其WriteString
方法,以手动将空白字符替换为其数字字符实体.该WriteString
方法也恰好是其中.NET实体化有不允许出现在文本节点,如语法标记字符的地方&
,<
和>
,它们分别实体化于&
,<
,和>
.
既然XmlWriter
是抽象的,我们将派生自XmlTextWriter
以避免必须实现前一类的所有抽象方法.这是一个快速而肮脏的实现:
public class EntitizingXmlWriter : XmlTextWriter
{
public EntitizingXmlWriter(TextWriter writer) :
base(writer)
{ }
public override void WriteString(string text)
{
foreach (char c in text)
{
switch (c)
{
case '\r':
case '\n':
case '\t':
base.WriteCharEntity(c);
break;
default:
base.WriteString(c.ToString());
break;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果打算在生产环境中使用,您需要取消该c.ToString()
部件,因为它的效率非常低.您可以通过批处理原始的子字符串来优化代码,这些子字符串text
不包含您要授权的任何字符,并将它们组合到一个base.WriteString
调用中.
警告:以下天真实现不起作用,因为基本WriteString
方法将替换任何&
字符&
,从而导致\r
扩展为&#xA;
.
public override void WriteString(string text)
{
text = text.Replace("\r", "
");
text = text.Replace("\n", "
");
text = text.Replace("\t", "	");
base.WriteString(text);
}
Run Code Online (Sandbox Code Playgroud)
最后,要将您保存XDocument
到目标文件或流中,只需使用以下代码段:
using (var textWriter = new StreamWriter(destination))
using (var xmlWriter = new EntitizingXmlWriter(textWriter))
document.Save(xmlWriter);
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助!
编辑:作为参考,这是被覆盖WriteString
方法的优化版本:
public override void WriteString(string text)
{
// The start index of the next substring containing only non-entitized characters.
int start = 0;
// The index of the current character being checked.
for (int curr = 0; curr < text.Length; ++curr)
{
// Check whether the current character should be entitized.
char chr = text[curr];
if (chr == '\r' || chr == '\n' || chr == '\t')
{
// Write the previous substring of non-entitized characters.
if (start < curr)
base.WriteString(text.Substring(start, curr - start));
// Write current character, entitized.
base.WriteCharEntity(chr);
// Next substring of non-entitized characters tentatively starts
// immediately beyond current character.
start = curr + 1;
}
}
// Write the trailing substring of non-entitized characters.
if (start < text.Length)
base.WriteString(text.Substring(start, text.Length - start));
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3300 次 |
最近记录: |