我该如何处理这个Xml解析错误?

时间:2012-07-06 13:11:27

标签: c# xml-parsing

考虑以下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,我无法控制他们给我的东西。

1 个答案:

答案 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("<", "&lt;");

                                    // 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   &lt;yyy zzz aaa</elem3>
                </elem1>
            </base>";
            Debug.Assert(result == expectedResult);