#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 #, если вы не знаете, что тег элемента больше не изменится на следующей итерации. И если ваш код полагается на какие-то определенные теги элементов, определите их как строковые константы один раз в центральном месте вашего кода, чтобы вы могли легко изменять их всякий раз, когда тег переименовывается внешней командой.