考虑以下C#代码:
using System.Xml.Linq;
namespace TestXmlParse
{
class Program
{
static void Main(string[] args)
{
var testxml =
@"<base>
<elem1 number='1'>
<elem2>yyy</elem2>
<elem3>xxx <yyy zzz aaa</elem3>
</elem1>
</base>";
XDocument.Parse(testxml);
}
}
}
我在解析时遇到System.Xml.XmlException,当然抱怨elem3。错误消息是这样的:
System.Xml.XmlException was unhandled
Message='aaa' is an unexpected token. The expected token is '='. Line 4, position 59.
Source=System.Xml
LineNumber=4
LinePosition=59
显然这不是真正的Xml(我们从第三方获取xml),而最好的答案是第三方在将它们发送给我们之前清理它们的xml,有没有其他方式我可能在把它交给解析器之前修复这个xml?我设计了一种解决这个问题的黑客方法;捕获异常并使用它告诉我在哪里需要查找应该转义的字符。我希望有一些更优雅和全面的东西。
欢迎任何建议。
如果这是一个骗局,请指出其他问题;我会亲自关闭它。我对答案比对任何业力增益更感兴趣。
编辑:
我想我没有像我希望的那样清楚地表达我的问题。我知道“&lt;”在elem3中是不正确的;我试图找到一种优雅的方法来检测(并纠正)任何形式错误的xml 之前我尝试解析。正如我所说,我从第三方获得这个xml,我无法控制他们给我的东西。
答案 0 :(得分:2)
我建议您不要操纵您收到的数据。如果它无效则是您客户的问题。
编辑输入使其有效xml会导致严重问题,例如:您可能最终处理错误的数据而不是抛出错误(因为您尽力使xml有效,但这可能会导致不同的数据)。
<强> [编辑] 强> 我仍然认为这不是一个好主意,但有时你必须做你必须做的事情。
这是一个非常简单的类,它解析输入并替换invald开始标记。您可以使用正则表达式(我不擅长)并且此解决方案不完整,例如根据你的要求(或者说你得到的坏xml)你将不得不采用它(例如扫描完整的xml元素而不是只有“&lt;”和“&gt;”括号,把CDATA放在一个内部文本的周围。节点等。)
我只想说明你如何做到这一点,所以如果它很慢/有错误请不要抱怨(正如我所提到的,我不会这样做)。
class XmlCleaner
{
public void Clean(Stream sourceStream, Stream targetStream)
{
const char openingIndicator = '<';
const char closingIndicator = '>';
const int bufferSize = 1024;
long length = sourceStream.Length;
char[] buffer = new char[bufferSize];
bool startTagFound = false;
StringBuilder writeBuffer = new StringBuilder();
using(var reader = new StreamReader(sourceStream))
{
var writer = new StreamWriter(targetStream);
try
{
while (reader.Read(buffer, 0, bufferSize) > 0)
{
foreach (var c in buffer)
{
if (c == openingIndicator)
{
if (startTagFound)
{
// we have 2 following opening tags without a closing one
// just replace the first one
writeBuffer = writeBuffer.Replace("<", "<");
// append the new one
writeBuffer.Append(c);
}
else
{
startTagFound = true;
writeBuffer.Append(c);
}
}
else if (c == closingIndicator)
{
startTagFound = false;
// write writebuffer...
writeBuffer.Append(c);
writer.Write(writeBuffer.ToString());
writeBuffer.Clear();
}
else
{
writeBuffer.Append(c);
}
}
}
}
finally
{
// unfortunately the streamwriter's dispose method closes the underlying stream, so e just flush it
writer.Flush();
}
}
}
测试它:
var testxml =
@"<base>
<elem1 number='1'>
<elem2>yyy</elem2>
<elem3>xxx <yyy zzz aaa</elem3>
</elem1>
</base>";
string result;
using (var source = new MemoryStream(Encoding.ASCII.GetBytes(testxml)))
using(var target = new MemoryStream()) {
XmlCleaner cleaner = new XmlCleaner();
cleaner.Clean(source, target);
target.Position = 0;
using (var reader = new StreamReader(target))
{
result = reader.ReadToEnd();
}
}
XDocument.Parse(result);
var expectedResult =
@"<base>
<elem1 number='1'>
<elem2>yyy</elem2>
<elem3>xxx <yyy zzz aaa</elem3>
</elem1>
</base>";
Debug.Assert(result == expectedResult);