如何使用不断变化的 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) && 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 && reader.NodeType != XmlNodeType.Attribute &&
reader.NodeType != XmlNodeType.EndElement && 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;
}
Scenario:
Input:
- An XML file whose structure constantly changes on the mercy of external team.
- It's not a well formed XML.
- Element Tags change too sometime.
Required Output:
- A Object of predefined class which used by other parts of the application
Problem:
- How do i map Changing Element tags with fixed Class names.
C# language.
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) && 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 && reader.NodeType != XmlNodeType.Attribute &&
reader.NodeType != XmlNodeType.EndElement && 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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您应该强制外部团队至少为您提供一个语法正确的 XML 文件。这是一个组织问题,但如果你不能解决这个问题,其他一切都没多大意义。
使用 XmlDocument。在代码中动态访问内容,并处理运行时缺少标签或块的情况。只要您不知道元素标签在下一次迭代中不会再发生变化,就避免将元素标签映射到 C# 类中静态定义的属性。如果您的代码依赖于某些特定的元素标签,请在代码的中心位置将它们定义为字符串常量,以便在外部团队重命名标签时轻松更改它们。
First, you should force your external team to give you at least a syntactically correct XML file. This is an organizational issue, but if you cannot solve this, everything else does not make much sense.
Use an XmlDocument. Access the content dynamically in your code, and deal with the situation when there is a missing tag or block at runtime. Avoid mapping of element tags to statically defined attributes in a C# class as long as you don't know the element tag won't change any more in the next iteration. And if your code relies on some specific element tags, define them as string constants once in a central place in your code so you change them easily whenever a tag is renamed by the external team.