前言

最近由于项目需求,需要读写操作XML文件,并且存储的XML文件格式会随着导入的数据不同而随时改变(当然导入的数据还是有一定约束的),这样我们要预先定义好XML文件的格式就不太现实了,如何实现不管导入的数据如何变化,我都能正确的把数据解析出来,这就是要实现的动态的XML文件读写操作!如果大家有更好的方式欢迎交流!

具体实现

本文所实现的读写XML文件是使用序列话的方式,具体博文请参考:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html,当然如果只是序列化操作XML文件的话,只需要看这篇博文,也就不需要这篇文章了!

了解了如何序列化操作XML文件了话,你就会知道,要想写入和读取XML文件,我们就需要定义好一个数据类型(这一点很重要),但是问题就出现了,如果我们在编程时就定义好一个数据类型了,当导入的数据改变了,这个数据类型就不适合了,接下来就来解决这个问题

实现一:根据需求创建动态数据类型

废话先不多说,先上代码,代码说话,往往比语言来的直接也更容易懂

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Diagnostics; namespace XMLDemo
{
public class ReflectClass
{
//保存动态生成并编译的类的type对象
Type theType = null;
//保存动态生成类的实例
object theClass = null;
public Assembly DoOperation()
{
//未初始化
if (theType == null)
{
//初始化
return GenerateCode();
}
return null;
}
private Assembly GenerateCode()
{
//文件名
string fileName = "XmlClass";
////从编译好的dll文件load一个Assembly
//Assembly a = Assembly.LoadFrom(fileName + ".dll"); //从编译好的dll文件load一个Assembly
byte[] filedata = File.ReadAllBytes(fileName + ".dll");
Assembly a = Assembly.Load(filedata); //此方法在使用完dll文件后会自动释放资源
return a;
}
public Assembly DoOperation(List<ExcelData> list)
{
//未初始化
if (theType == null)
{
//初始化
return GenerateCode(list);
}
return null;
}
private Assembly GenerateCode(List<ExcelData> list)
{
//文件名
string fileName = "XmlClass";
//打开文件,如果不存在,则创建
Stream s = File.Open(fileName + ".cs", FileMode.Create);
//创建一个StreamWriter来写入数据
StreamWriter wrtr = new StreamWriter(s); #region 写入动态创建类的源代码
wrtr.WriteLine("// 动态创建的XmlClass类文件"); //类名,此类是为序列化读取XML文件创建的类文件
string className = "XmlClass";
wrtr.WriteLine("using System;");
wrtr.WriteLine("using System.Xml.Serialization;");
wrtr.WriteLine("public class {0}", className);
wrtr.WriteLine("{");
var ch = (from num in list select num.Mark).Distinct().ToList();
foreach (var n in ch)
{
wrtr.WriteLine("\tpublic " + n + " " + n);
wrtr.WriteLine("\t{");
wrtr.WriteLine("\t\tget;set;");
wrtr.WriteLine("\t}");
}
wrtr.WriteLine("}");
foreach (var n in ch)
{
wrtr.WriteLine("public class {0}", n);
wrtr.WriteLine("{");
var nlist = from num in list where (num.Mark == n) select num;
foreach (var m in nlist)
{
wrtr.WriteLine("\tpublic string " + m.Name);
wrtr.WriteLine("\t{");
wrtr.WriteLine("\t\tget;set;");
wrtr.WriteLine("\t}");
}
wrtr.WriteLine("}");
}
//关闭StreamWriter和文件
wrtr.Close();
s.Close();
#endregion //启动进程编译源文件
//指定参数
ProcessStartInfo psi = new ProcessStartInfo();
File.Delete(fileName + ".dll"); //重新导入数据时删除旧的DLL文件
//启动cmd.exe
psi.FileName = "cmd.exe";
//cmd.exe的参数,/c-close,完成后关闭;后为参数,指定cmd.exe使用csc来编译刚才生成的源文件
string compileString = "/c C:\\WINDOWS\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe /optimize+ /target:library {0}.cs";
psi.Arguments = String.Format(compileString, fileName);
//运行时的风格-最小化
psi.WindowStyle = ProcessWindowStyle.Minimized;
//启动进程
Process proc = Process.Start(psi);
//指定当前在此进程退出前等待
proc.WaitForExit();
//从编译好的dll文件load一个Assembly
byte[] filedata = File.ReadAllBytes(fileName + ".dll");
Assembly a = Assembly.Load(filedata); //此方法在使用完dll文件后会自动释放资源
//Assembly a = Assembly.LoadFrom(fileName + ".dll"); //这样使用dll文件会一直被占用,不会释放,造成重新生成前无法自动删除
//删除源文件
//File.Delete(fileName + ".cs");
return a;
}
}
}

以上 ReflectClass类帮助我们实现了通过导入的数据来创建一个供我们使用的编译好的类文件,下面主要讲如果通过这个使用这个编译好的dll文件来写入我们的XML文件

写入动态创建类的源代码那一块真是费了我不少脑细胞,感觉跟写模板类似的!(大家如果根据自己需求改写的话,应该就会体会到)

实现二:通过反射得到动态创建的数据类型并序列化写入XML文件

还是先上代码,XMLHelper帮助类可以去上面的连接去下载,也可以在我提供的Demo里下载,我只根据我的需要添加了一个方法,下面我的一个普通帮助类CommonHelper

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO; namespace XMLDemo
{
public class CommonHelper
{
/// <summary>
/// 写文件
/// </summary>
/// <param name="filePath">文件名</param>
/// <param name="Context">写的内容</param>
/// <returns>是否成功</returns>
public static bool FileWriter(string filePath, string Context)
{
try
{
//打开文件,如果不存在,则创建
Stream s = File.Open(filePath, FileMode.Create);
//创建一个StreamWriter来写入数据
StreamWriter wrtr = new StreamWriter(s);
//写入动态创建类的源代码
wrtr.WriteLine(Context);
wrtr.Close();
s.Close();
return true;
}
catch (Exception)
{ return false;
}
}
/// <summary>
/// 读文件
/// </summary>
/// <param name="filePath">文件名</param>
/// <returns>返回读的内容</returns>
public static string FileRead(string filePath)
{
try
{
//打开文件,如果不存在,则创建
Stream s1 = File.Open(filePath, FileMode.Open);
//创建一个StreamWriter来写入数据
StreamReader reader = new StreamReader(s1);
//写入动态创建类的源代码
string xml = reader.ReadToEnd();
reader.Close();
s1.Close();
return xml;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 根据数据创建类,并得到动态创建类的集合
/// </summary>
/// <param name="list">导入的数据集合</param>
/// <returns>返回类的集合</returns>
public static List<object> GetXmlClassInstances(List<ExcelData> list)
{
List<object> o = new List<object>();
ReflectClass t = new ReflectClass();
var assemblys = t.DoOperation(list);
Type[] types = assemblys.GetExportedTypes();
foreach (Type type in types)
{
o.Add(assemblys.CreateInstance(type.Name));
}
return o;
}
/// <summary>
/// 得到动态创建类的集合
/// </summary>
/// <returns></returns>
public static List<object> GetXmlClassInstances()
{
List<object> o = new List<object>();
ReflectClass t = new ReflectClass();
var assemblys = t.DoOperation();
Type[] types = assemblys.GetExportedTypes();
foreach (Type type in types)
{
o.Add(assemblys.CreateInstance(type.Name));
}
return o;
}
}
}

主要是一个GetXmlClassInstances(List<ExcelData> list):根据数据创建类,并得到动态创建类的集合

一个是GetXmlClassInstances():得到已经创建好的动态类的集合

下面是一个数据实体类,也就是我们所规范的数据格式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace XMLDemo
{
/// <summary>
/// 数据实体类
/// </summary>
public class ExcelData
{
/// <summary>
/// 根节点
/// </summary>
public string Mark { get; set; }
/// <summary>
/// 子节点
/// </summary>
public string Name { get; set; } /// <summary>
/// 子节点对应值
/// </summary>
public string Value { get; set; }
}
}

准备工作就绪后就可以进行XML文件的写入了,依然是废话不多说,上代码:

       private void btnWrite_Click(object sender, EventArgs e)
{
List<object> XmlClassInstances = CommonHelper.GetXmlClassInstances(listData);
if (InsertXmlClass(XmlClassInstances, listData) == false)
{
MessageBox.Show("数据导入失败");
return;
}
}
private bool InsertXmlClass(List<object> o, List<ExcelData> list)
{ for (int i = ; i < o.Count; i++)
{
//给类的属性赋值
var nlist = from num in list where (num.Mark == o[i].GetType().Name) select num;
foreach (var model in nlist)
{
PropertyInfo propinfo = o[i].GetType().GetProperty(model.Name);
propinfo.SetValue(o[i], model.Value, null);
}
}
var XmlClass = o[]; //默认第一个类为主体类,主体类的属性为其他类
var tt = XmlClass.GetType();
for (int i = ; i < o.Count; i++)
{
//给主体类的属性赋值
PropertyInfo propinfo = tt.GetProperty(o[i].GetType().Name);
propinfo.SetValue(XmlClass, o[i], null);
}
//序列化主体类,得到序列化字符串
string xml = XmlHelper.XmlSerialize(XmlClass, Encoding.UTF8);
string FilePath = "QMSPlan.XML";
if (CommonHelper.FileWriter(FilePath, xml) == false)
{
return false;
}
return true;
}

上面的代码可能有点绕,大家可以单步调试去理解,当时写的时候崩溃了几次,最后才调试好,不知道有什么更好的写法!

实现三:序列化读出XML文件

感觉用语言都表达不出来,还是直接上代码吧

       private void btnRead_Click(object sender, EventArgs e)
{
List<ExcelData> listRead = new List<ExcelData>();
var XmlClass = GetXmlClass();
if (XmlClass == null)
{
MessageBox.Show("数据读取失败");
return;
}
//获取所有主体类的属性(即所有的子类集合)
PropertyInfo[] ps = XmlClass.GetType().GetProperties();
foreach (var n in ps)
{
//得到子类
var m = n.GetValue(XmlClass, null);
//获取子类的所有属性
PropertyInfo[] x = m.GetType().GetProperties();
foreach (var y in x)
{
ExcelData model = new ExcelData();
model.Mark = m.GetType().Name;
model.Name = y.Name;
model.Value = y.GetValue(m, null).ToString();
listRead.Add(model);
}
}
}
private object GetXmlClass()
{
List<object> o = CommonHelper.GetXmlClassInstances();
var XmlClass = o[]; //默认第一个类为主体类 string FilePath = "QMSPlan.XML";
string xml = CommonHelper.FileRead(FilePath);
if (xml == null)
{
return null;
}
XmlClass = XmlHelper.XmlDeserialize(XmlClass.GetType(), xml, Encoding.UTF8);
return XmlClass;
}

上面大量运用了反射的特性,就是因为实现动态读取数据类型而造成的!

结束语

可能大家看完觉得完全没必要这样做,并且大量这样的操作会影响性能,不过这样做实实在在解决了我项目中的难点,实现了根据导入数据的不同,可以实现正确的XML的读写操作,

总结一下:以上总共有三个知识点的应用

1.动态创建数据类型并在程序中使用

2.序列话读写XML文件

3.利用反射特性实现

最后附上demo下载地址:http://files.cnblogs.com/beimeng/XML.demo.rar

实现动态的XML文件读写操作(依然带干货)的更多相关文章

  1. 实现动态的XML文件读写操作

    实现动态的XML文件读写操作(依然带干货) 前言 最近由于项目需求,需要读写操作XML文件,并且存储的XML文件格式会随着导入的数据不同而随时改变(当然导入的数据还是有一定约束的),这样我们要预先定义 ...

  2. ios 简单的plist文件读写操作(Document和NSUserDefaults)

    最近遇到ios上文件读写操作的有关知识,记录下来,以便以后查阅,同时分享与大家. 一,简单介绍一下常用的plist文件. 全名是:Property List,属性列表文件,它是一种用来存储串行化后的对 ...

  3. QXmlStreamReader/QXmlStreamWriter实现Qt下xml文件读写

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QXmlStreamReader/QXmlStreamWriter实现Qt下xml文件读写   ...

  4. c语言文件读写操作总结

    C语言文件读写操作总结 C语言文件操作 一.标准文件的读写 1.文件的打开 fopen() 文件的打开操作表示将给用户指定的文件在内存分配一个FILE结构区,并将该结构的指针返回给用户程序,以后用户程 ...

  5. [转]Android - 文件读写操作 总结

     转自:http://blog.csdn.net/ztp800201/article/details/7322110 Android - 文件读写操作 总结 分类: Android2012-03-05 ...

  6. PHP文件读写操作之文件写入代码

    在PHP网站开发中,存储数据通常有两种方式,一种以文本文件方式存储,比如txt文件,一种是以数据库方式存储,比如Mysql,相对于数据库存储,文件存储并没有什么优势,但是文件读写操作在基本的PHP开发 ...

  7. 更新java对xml文件的操作

    //更新java在xml文件中操作的内容 public static void upda(Document doc) throws Exception{ //创建一个TransformerFactor ...

  8. Java 字节流实现文件读写操作(InputStream-OutputStream)

    Java 字节流实现文件读写操作(InputStream-OutputStream) 备注:字节流比字符流底层,但是效率底下. 字符流地址:http://pengyan5945.iteye.com/b ...

  9. Java 字符流实现文件读写操作(FileReader-FileWriter)

    Java 字符流实现文件读写操作(FileReader-FileWriter) 备注:字符流效率高,但是没有字节流底层 字节流地址:http://pengyan5945.iteye.com/blog/ ...

随机推荐

  1. c++中左值的含义

    <<cpp primer plus 6th edition>>中的原文(Chapter 8 Adventures in Functions): What is an lvalu ...

  2. OC NSMutableString的使用

    #pragma mark 可变字符串的创建 void stringCreate() { // 预先分配10个字数的存储空间 NSMutableString *str = [[NSMutableStri ...

  3. BZOJ2141:排队(分块,树状数组)

    Description 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们 ...

  4. springmvc(5)拦截器

    1.什么是拦截器 是指通过统一的拦截从浏览器发送到服务器的请求来完成相应服务增强 2.拦截器的基本原理 可以通过配置过滤器解决乱码问题 和过滤器非常相似 3.搭建工程 注意jar包 此时的工程是完成后 ...

  5. 十一、IntelliJ IDEA 中的版本控制介绍(上)

    咱们已经了解了很多关于 IntelliJ IDEA 的使用方法,至少可以独立的运用 IntelliJ IDEA 进行项目开发啦!但是一个人进行项目开发更趋向于理想化,更多的则是团队协同开发.这时,咱们 ...

  6. HDU 1735 字数统计(模拟+一点点贪心的思想)

    题目戳我 字数统计 Time Limit: 1000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  7. Etherlab debian安装记录

    debian wheezy 7.11(虚拟机安装选择桥接网卡) #set ustc source #apt-get install sudo #nano /etc/sudoers;add userNa ...

  8. JQuery手写一个简单的分页

    效果图: 大概思路:使用ul进行初始布局,每一次点击事件改变li里的值.完整的代码在gitup上:https://github.com/anxizhihai/Paging.gitcss部分: html ...

  9. Oracle中table数据数据类型

    function F_ReturnDescription(varID in varchar2) return varchar2 is numDataCount ); mytable ly_family ...

  10. Oracle 左连接(+)加号用法及常用语法之间的关系

    本文目的: 通过分析左连接(+)加号的写法和一些常用语法之间的联系,了解到Oracle 加号(+)的用法 分析步骤: 1.首先创建测试表的结构: create table test_left_a (a ...