3

次の 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 を修正する他の方法はありますか?パーサーに渡す前に?これを修正するハックな方法を考案しました。例外をキャッチし、それを使用して、エスケープする必要がある文字を探す必要がある場所を教えてください。もう少しエレガントで包括的なものを望んでいました。

どんな提案でも大歓迎です。

これがだまされている場合は、他の質問を教えてください。私はこれを自分で閉じます。私はカルマの獲得よりも答えに興味があります。

編集:

私は自分の質問を私が望んでいたほど明確にしなかったと思います. elem3 の「<」が間違っていることはわかっています。解析を試みるに、そのような不適切な形式の xml を検出 (および修正) するエレガントな方法を見つけようとしています。私が言うように、私はこの xml をサードパーティから入手しましたが、彼らが私に与えるものを制御することはできません。

4

1 に答える 1

2

受信したデータを操作しないことをお勧めします。無効な場合は、クライアントの問題です。

有効なxmlになるように入力を編集すると、重大な問題が発生する可能性があります。たとえば、エラーをスローする代わりに、間違ったデータを処理してしまう可能性があります(xmlを有効にするために最善を尽くしたため、データが異なる可能性があります)。


[編集] それはまだ良い考えではないと思いますが、時々あなたはあなたがしなければならないことをしなければなりません。

これは、入力を解析し、無効な開始タグを置き換える非常に単純なクラスです。正規表現(私は得意ではありません)を使用してこれを行うことができますが、このソリューションは完全ではありません。たとえば、要件に応じて(または、取得した悪いxmlを言うと)、それを採用する必要があります(たとえば、完全なxml要素をスキャンする) 「<」と「>」の括弧だけでなく、ノードの内部テキストの周りに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);
于 2012-07-06T13:15:37.700 に答える