Как использовать постоянно меняющийся XML

#c# #xml-parsing

#c# #синтаксический анализ XML

Вопрос:

Сценарий:

Ввод:

  • XML-файл, структура которого постоянно меняется по милости внешней команды.
  • Это неправильно сформированный XML.
  • Теги элементов тоже иногда меняются.

Требуемый вывод:

  • Объект предопределенного класса, который используется другими частями приложения

Проблема:

  • Как мне сопоставить изменяющиеся теги элементов с фиксированными именами классов.

Язык C #.

 enter code here
ReadIn("input.xml");

public static GbtInfo ReadIn(string path)
    {
        using (XmlReader reader = new XmlTextReader(path))
        {
            reader.ReadToDescendant("SYSTEM");
            return Serializers.ParseNode<GbtInfo>(reader);
        }
    }

public static T ParseNode<T>(XmlReader reader)
{
    Type t = typeof(T);
    return (T)ParseNode(t, reader);
}

public static object ParseNode(Type type, XmlReader reader)
{
    var instance = Activator.CreateInstance(type);

    IXmlSerializable xmlSerializable = instance as IXmlSerializable;
    if (xmlSerializable != null)
    xmlSerializable.ReadXml(reader);
    return instance;
}

public static object ParseNode(string name_space, string elementName, XmlReader reader)
{
    Type t = Type.GetType(name_space   "."   elementName, false, true);
    return ParseNode(t, reader);
}





public void ReadXml(System.Xml.XmlReader reader)
    {
        this.reader = reader;
        string nextElement;
        parent = reader.Name;
        PropertyInfo propertyinfo = null;

        //Setting a flag if the current node is empty.
        bool isEmptyElement = reader.IsEmptyElement;
        //Code that parses the attributes out of the Node.
        if (reader.HasAttributes)
        {
            for (int i = 0; i < reader.AttributeCount; i  )
            {
                reader.MoveToAttribute(i);
                nextElement = Utilities.RemoveSpecialChar(reader.Name);
                propertyinfo = (GetType()).GetProperty(nextElement, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                if (propertyinfo != null)
                    propertyinfo.SetValue(this, reader.Value, null);
                else
                    PrintError(nextElement);


            }
        }
        if (!isEmptyElement)//if the element is not empty get all the children 
        {
            reader.Read();
            Utilities.SkipToContent(reader);
            while (!(reader.Name.Equals(parent) amp;amp; reader.NodeType == XmlNodeType.EndElement))
            {
                reader.MoveToContent();
                //Case when Node Element is an object type with string 
                if (reader.NodeType == XmlNodeType.Text)
                {
                    propertyinfo = (GetType()).GetProperty("Value", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

                    if (propertyinfo != null)
                        propertyinfo.SetValue(this, reader.Value, null);
                    else
                        PrintError("Value");

                    //Testing Console.WriteLine(nextelement   " => "   reader.Value);
                    reader.Read();
                    Utilities.SkipToContent(reader);
                }
                if (reader.NodeType == XmlNodeType.Element)
                {
                    nextElement = Utilities.RemoveSpecialChar(reader.Name);
                    propertyinfo = (GetType()).GetProperty(nextElement, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    if (propertyinfo != null)
                    {
                        if (propertyinfo.PropertyType.FullName.Equals("System.String"))
                        {
                            reader.Read();//read to get the text
                            if (reader.NodeType != XmlNodeType.Text)
                                throw new InvalidOperationException("Special Case encountered check XML");
                            propertyinfo.SetValue(this, reader.Value, null);
                            //Testing Console.WriteLine(reader.Value);
                            reader.ReadToNextSibling("dummy");//this will read to the parent end tag
                            reader.Read();
                            Utilities.SkipToContent(reader);
                        }
                        else
                        {

                            System.Collections.IList list = propertyinfo.GetValue(this, null) as System.Collections.IList;
                            if (list != null)
                            {
                                list.Add(Serializers.ParseNode(Namespace, nextElement, reader));

                            }
                            else
                            {
                                propertyinfo.SetValue(this, Serializers.ParseNode(Namespace, nextElement, reader), null);
                            }
                        }
                    }
                    else
                    {
                        PrintError(nextElement);
                        reader.ReadToNextSibling();
                    }
                }
            }
        }

        //move to the next element
        reader.Read();
        Utilities.SkipToContent(reader);
    }


    // Utilities Method

      private void PrintError(string errorElement)
        {
            IXmlLineInfo info = reader as IXmlLineInfo;
            Log.LogIt("The attribute "   errorElement   " does not exist under "   parent   " Error Occurred at Line "
                        info.LineNumber   " Col "   info.LinePosition, LogMessageType.Warning);
            info = null;
    }

     public static XmlReader SkipToContent(XmlReader reader)
        {
            int count = 0;
            while (reader.NodeType != XmlNodeType.Element amp;amp; reader.NodeType != XmlNodeType.Attribute amp;amp;
                   reader.NodeType != XmlNodeType.EndElement amp;amp; reader.NodeType != XmlNodeType.Text)
            {
                reader.Read(); count  ;
                if (count > 2)
                {
                    //Console.WriteLine(" Stuck");
                    if (reader.EOF)
                    {
                        break;
                    }
                }
            }
            return reader;
        }

/// <summary>
/// Removes special symbols like "-","_","." from Node element name inorder to match it with the respective objects. 
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string RemoveSpecialChar(string str)
        {
            str = str.Replace("-", "");
            str = str.Replace(".", "");
            str = str.Replace("_", "");
            return str;
    }
  

Комментарии:

1. Звучит как злоупотребление XML.

2. «Это неправильно сформированный XML» — значит, вы спрашиваете «как разобраться в неформатированных текстовых данных»? Множество людей работают над проблемой, и до сих пор не найдено особенно простого и распространенного решения :).

3. Само собой разумеется, что ваша внешняя команда должна следовать спецификации, и если эта спецификация не соблюдается точно, XML необходимо вернуть внешней команде для исправления. Никто другой не должен мириться с такими глупостями.

4. «плохо сформированный xml» — попросите своего руководителя группы немного расширить и отодвинуть назад.

5. Если вам не интересна часть вашего документа, изменяющая схему, а вам интересна статическая часть схемы, вы можете выполнить поиск XPath (возможно, рекурсивно — начните с root, найдите все элементы-предки с определенным именем) для интересующей вас части. Если есть соглашение об изменениях, вы можете изучить некоторую схему генерации кода для создания класса из XML, а затем использовать Automapper для сопоставления с вашим классом. Если вы должны полагаться непосредственно на части схемы, которые изменяются, без какого-либо шаблона для изменений, тогда вам, возможно, придется решать эту проблему mano-a-mano…

Ответ №1:

Во-первых, вы должны заставить свою внешнюю команду предоставить вам хотя бы синтаксически корректный XML-файл. Это организационная проблема, но если вы не можете решить это, все остальное не имеет особого смысла.

Объект предопределенного класса, который используется другими частями приложения

Используйте XML-документ. Получите доступ к содержимому динамически в вашем коде и разберитесь с ситуацией, когда во время выполнения отсутствует тег или блок. Избегайте сопоставления тегов элементов со статически определенными атрибутами в классе C #, если вы не знаете, что тег элемента больше не изменится на следующей итерации. И если ваш код полагается на какие-то определенные теги элементов, определите их как строковые константы один раз в центральном месте вашего кода, чтобы вы могли легко изменять их всякий раз, когда тег переименовывается внешней командой.