XMLReader Недопустимое исключение символов XML - PullRequest
1 голос
/ 12 апреля 2019

Я анализирую большой файл XML ~ 500 МБ, и он содержит какой-то недопустимый символ XML 0x07, так что вы можете себе представить, что происходит, XMLReader вызывает исключение Недопустимый символ XML, для обработки которого мы выделили Streamв StreamReader и использовал Regex.Replace и записал результат в память с помощью StreamWriter и передал чистую версию обратно в XMLReader, теперь я хотел бы избежать этого и пропустить этот грязный тег из XMLReader напрямую, мой вопросесли есть что-то для достижения этого, ниже приведен фрагмент кода, где я пытаюсь это сделать, но он выдает исключение в этой строке var node = (XElement)XNode.ReadFrom(xr);

        protected override IEnumerable<XElement> StreamReader(Stream stream, string elementName)
    {

        var arrTag = elementName.Split('|').ToList();
        using (var xr = XmlReader.Create(stream, new XmlReaderSettings { CheckCharacters = false }))
        {
            while (xr.Read())
            {
                if (xr.NodeType == XmlNodeType.Element && arrTag.Contains(xr.Name))
                {
                    var node = (XElement)XNode.ReadFrom(xr);
                    node.ReplaceWith(node.Elements().Where(e => e.Name != "DaylightSaveInfo"));
                    yield return node;
                }
            }
            xr.Close();
        }
 }

XML SAMPLE, недопустимый атрибут DaylightSaveInfo

<?xml version="1.0" encoding="ISO-8859-1"?>
<LATree>
<LA className="BTT00NE" fdn="NE=9739">
    <attr name="fdn">NE=9739</attr>
    <attr name="IP">10.157.144.100</attr>
    <attr name="realLatitude">0D0&apos;0&quot;S</attr>
    <attr name="realLongitude">0D0&apos;0&quot;W</attr>
    <attr name="DaylightSaveInfo">NO</attr>
</LA>
</LATree>

BEL Character

Ответы [ 2 ]

2 голосов
/ 12 апреля 2019

Я только что увидел, что Джон Скит что-то написал по этому поводу, поэтому я не могу взять кредит на самом деле, но, поскольку его статус на SO намного выше моего, я мог бы получить одно или два очка за его написание. :)

Сначала я написал класс, который перегружает класс TextReader. (Некоторые справочные материалы в ссылках.)

https://www.w3.org/TR/xml/#NT-Char

https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/io/textreader.cs

class FilterInvalidXmlReader : System.IO.TextReader
{
  private System.IO.StreamReader _streamReader;

  public System.IO.Stream BaseStream => _streamReader.BaseStream;

  public FilterInvalidXmlReader(System.IO.Stream stream) => _streamReader = new System.IO.StreamReader(stream);

  public override void Close() => _streamReader.Close();

  protected override void Dispose(bool disposing) => _streamReader.Dispose();

  public override int Peek()
  {
    var peek = _streamReader.Peek();

    while (IsInvalid(peek, true))
    {
      _streamReader.Read();

      peek = _streamReader.Peek();
    }

    return peek;
  }

  public override int Read()
  {
    var read = _streamReader.Read();

    while (IsInvalid(read, true))
    {
      read = _streamReader.Read();
    }

    return read;
  }


  public static bool IsInvalid(int c, bool invalidateCompatibilityCharacters)
  {
    if (c == -1)
    {
      return false;
    }

    if (invalidateCompatibilityCharacters && ((c >= 0x7F && c <= 0x84) || (c >= 0x86 && c <= 0x9F) || (c >= 0xFDD0 && c <= 0xFDEF)))
    {
      return true;
    }

    if (c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD))
    {
      return false;
    }

    return true;
  }
}

Затем я создал консольное приложение и в основном поставил:

  using (var memoryStream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes("<Test><GoodAttribute>a\u0009b</GoodAttribute><BadAttribute>c\u0007d</BadAttribute></Test>")))
  {
    using (var xmlFilteredTextReader = new FilterInvalidXmlReader(memoryStream))
    {
      using (var xr = System.Xml.XmlReader.Create(xmlFilteredTextReader))
      {
        while (xr.Read())
        {
          if (xr.NodeType == System.Xml.XmlNodeType.Element)
          {
            var xe = System.Xml.Linq.XElement.ReadFrom(xr);

            System.Console.WriteLine(xe.ToString());
          }
        }
      }
    }
  }

Надеюсь, это могло бы помочь или, по крайней мере, дать некоторую начальную точку.

0 голосов
/ 12 апреля 2019

Следующий код xml linq работает без ошибок. Я использовал в XML-файле после "НЕТ":

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication108
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.CheckCharacters = false;
            XmlReader reader = XmlReader.Create(FILENAME, settings);

            XDocument doc = XDocument.Load(reader);

            Dictionary<string, string> dict = doc.Descendants("attr")
                .GroupBy(x => (string)x.Attribute("name"), y => (string)y)
                .ToDictionary(x => x.Key, y => y.FirstOrDefault());

        }
    }

}
...