using (var ms = new MemoryStream())
{
ClassA tryXml = new ClassA {Key = "A", Value = "1"}
XmlSerializer serializer = new XmlSerializer(typeof (ClassA))
serializer.Serialize(ms, tryXml)
var xmlClass = new XmlDocument()
xmlClass.LoadXml(Encoding.UTF8.GetString(ms.ToArray()))
var xmlDocument = new XmlDocument()
xmlDocument.Load("C:\\temp\\TryXml.xml")
XmlElement element = xmlDocument.CreateElement("ClassA")
element.InnerXml = xmlClass.DocumentElement.InnerXml
xmlDocument.SelectSingleNode("/configuration").AppendChild(element)
xmlDocument.Save("C:\\temp\\TryXml.xml")
前阵子在写LINQ Douban的时候碰到关于XML序列化的场景 通过Douban api添加和更新数据的时候都需要Post一个xml entry 如
添加活动(xml中用%%括起来的部分是需要填写的部分 为了精简删除了xmlns)
view sourceprint? <?xml version= encoding= UTF ?>
<entry>
<title>%title%</title>
<category scheme= term=
<content>%Content%</content>
<db:attribute name= invite_only >%IsInviteOnly%</db:attribute>
<db:attribute name= can_invite >%CanInvite%</db:attribute>
<gd:when endTime= %Duration End% startTime= %Duration Start% />
<gd:where valueString= %Where% />
</entry>
一下子能想到的方法有两个——通过XmlSerializer或在entity class上实现IXmlSerializable接口来实现 但很快发现有几个问题没法解决
XmlSerializer不支持泛型集合的序列化 而我在定义entity class时用了不少IList和IDictionary 如db:attribute我就定义成IDictionary
XmlSerializer只能生成完整的element和attribute 像上面那段xml里<category>节点term属性里变化的只有后面%Category%部分 这就没法生成了
存在添加和更新的post内容不一样的情况 这就意味着同一个entity class 存在多种序列化方案
douban的xml entry格式可能会更改 而我不希望因此而更改代码
想来想去 最好的方法是通过XML模板+反射(简称XMl模板替换)来生成了 就像上面的xml etnry里面%%括起来的部分 替换掉就可以了 这样可以解决上述的四个问题 除了提供和XMLSerializer功能相同的序列化之外 XML模板替换还要满足下面这些要求
可以序列化实现IEnumerable的集合 这是最常用的集合 当然大多数的泛型集合也是应用了IEnumerable的
提供更灵活的替换 XmlSerializer实现的序列化顺序是 A(B(c)B)A 对于子对象的序列化只能是嵌套的模式 而XML模板替换可以实现任何层次的替换
为每种类型的对象提供通用的序列化方式 不需要任何Attribute定义 不需要修改对象的定义 对于给定的object和XML模板 通过发射获取属性值进行XML替换后生成XML内容 对于相同的object 提供不同的XML模板就能生成不同的XML
通过修改XML模板即可修改序列化结果
下面给出一个修改过的RSS的XML模板 这次Code Fun的目的是在最后实现这个模板的替换 并且完成一个能够实现上述功能的Helper class
特别的地方
<category>节点 通过 可访问子对象的属性 如果你希望获取Domain的长度可以写成 %Category Domain Length%
<noReplacement>节点 该节点不包含任何替换信息 当进行替换处理时应当忽略
<skipHours>节点 SkipHours是一个List<int>集合 我们希望能够根据SkipHours的值 展开多个<hour>节点
<as:scope>节点 <scope>是模板定义 声明<scope>节点内包含的子节点在Channel Items对象的作用域中 所有%%(不包括% /Category Name%)的属性都是对Items对象的属性访问 由于此处Items对象是List<RssItem>集合 所以将循环生成多个<item>Scope的含义类似于程序域 支持多个scope的嵌套 Scope定义不会出现在最后生成的xml中
<channelCategory>节点 <channelCategory>节点在Items的作用域中 但我们可以通过 / 访问外部scope的属性 类似dos文件路径 如果要访问上上级scope 则是 / / % /Category Name%表示访问Channel对象的Category属性的Name属性
view sourceprint? <channel>
<title>%Title%</title>
<link>%Link%</link>
<category domain= %Category Domain% >%Category Name%</category>
<noRelacement>不需要替换</noRelacement>
<skipHours>
<hour>%SkinHours%</hour>
</skipHours>
<as:scope xmlns:as= name= Items type= AllSharing Xml Rss RssItem >
<item>
<title>%Title%</title>
<link>%Link%</link>
<description>%Description%</description>
<channelCategory>% /Category Name%</channelCategory>
</item>
</as:scope>
</channel>
view sourceprint?
相关class定义
view sourceprint? public class RssChannel
{
public string Title { getset}
public string Link { getset}
public RssCategory Category { getset}
public IList<int>SkinHours { getset}
public IList<RssItem>Items { getset}
}
public class RssItem
{
public string Title { getset}
public string Link { getset}
}
public class RssCategory
{
public string Domain { getset}
public string Name { getset}
}
下面将一步步讨论如何实现XML模板的替换 会给出部分代码或伪代码 完整的代码在文章最后会给出下载
一 分析XML模板
XML模板替换最终要是要回归到用正则表达式替换掉所有的%% 但由于我们要处理的模板包括域 子属性访问 循环的信息 所以这不是仅仅的Regex Replace就可以搞定的 分析XML模板 就是要遍历XML模板生成一个Scope树 上面的XML模板可以生成下面的Scope树
XML模板中包含了的三种我们需要处理的元素
包含%%的XML Attribute
包含%%的XML Element以及Element的子节点
Scope节点
从上面的Scope树可以看出 像<noReplace>这样不需要替换的XML element或XML attribute被当作常量 没有包括在Scope树中
在Helper Class中分析Scope树的方法
view sourceprint? var scope = XmlTemplateScope Compile(xmlPath entityType)
xmlPath是模板文件的路径 entityType是Scope树用于分析的对象类型
二 对给定的object 生成XML
这里用了LINQ XML 首先用XDocument Load(xmlPath)的到XML模板文件的XDocument 然后根据Scope树对XDocument上的节点进行属性值替换 节点Value替换 增加节点(Repeat的节点) 幸运的是XDocument比XmlDocument方便太多了 实现起来非常快
在Helper Class中生成XML的方法
view sourceprint? var template = new XmlTemplate(xmlPath scope)
Console WriteLine(template Translate(entityObj))
template是线程安全的 根据需要你可以Cache起来 不用每次都生成Scope树 这样或许会减少部分性能消耗(未测试)
完整的代码如下(包含在Demo中)
view sourceprint? var channel = new RssChannel()
channel Title = this is channel title
channel Link =
channel Description = this is channel description
channel Category = new RssCategory()
channel Category Domain =
channel Category Name = this is channel category
channel SkipHours Add( )
channel SkipHours Add( )
channel SkipHours Add( )
channel Items Add(new RssItem { Title= Item Link= Link Description= Des })
channel Items Add(new RssItem { Title = Item Link = Link Description = Des })
channel Items Add(new RssItem { Title = Item Link = Link Description = Des })
var path = Path Combine(AppDomain CurrentDomain BaseDirectory rss xml )
var template = new XmlTemplate(
path XmlTemplateScope Compile(path typeof(RssChannel)))
template Translate(channel) Dump()
生成XML如下
view sourceprint? <channel>
<title>this is channel title</title>
<link>/link>
<category domain= this >>this is channel category</cate
gory>
<noRelacement>不需要替换</noRelacement>
<skipHours>
<hour></hour>
<hour></hour>
<hour></hour>
</skipHours>
<item>
<title>Item </title>
<link>Link </link>
<description>Des </description>
<channelCategory>this is channel category</channelCategory>
</item>
<item>
<title>Item </title>
<link>Link </link>
<description>Des </description>
<channelCategory>this is channel category</channelCategory>
</item>
<item>
<title>Item </title>
<link>Link </link>
<description>Des </description>
<channelCategory>this is channel category</channelCategory>
</item>
</channel>
lishixinzhi/Article/program/net/201311/12717给你个我写的用XML存配置的类。你自己看吧。 我看见你的第二题了。仔细看这个。简单的存储都有了
using System
using System.Collections.Generic
using System.Text
using System.Xml
using System.IO
using Microsoft.Win32
using System.Drawing
using System.Windows.Forms
namespace ChartFormat
{
class Config
{
#region 删除配置文件
/// <summary>
/// 删除配置文件
/// </summary>
/// <returns></returns>
public static bool DelConfig()
{
string m_strFullPath = ""
m_strFullPath = Directory.GetCurrentDirectory() + @"\" + "Config.config"
try
{
File.Delete(m_strFullPath)
return true
}
catch
{
return false
}
}
#endregion
#region 判断该路径文件是否存在 如果不存在则创建
/// <summary>
/// 判断该路径文件是否存在 如果不存在则创建
/// </summary>
/// <param name="m_strFullPath">路径</param>
public static void ConfigFile(string m_strFullPath)
{
StreamWriter sr = null
if (!System.IO.File.Exists(m_strFullPath))
{
try
{
sr = new StreamWriter(m_strFullPath)
sr.WriteLine("<?xml version=" + "\"" + "1.0" + "\"" + " encoding=" + "\"" + "utf-8" + "\"" + " ?>")
sr.WriteLine("<configuration>")
sr.WriteLine("<appSettings>")
sr.WriteLine("<add key=" + "\"" + "NameColour" + "\"" + " value=" + "\"" + "Blue" + "\"" + "/>")
sr.WriteLine("<add key=" + "\"" + "InfoColour" + "\"" + " value=" + "\"" + "Black" + "\"" + "/>")
sr.WriteLine("<add key=" + "\"" + "IsURL" + "\"" + " value=" + "\"" + "0" + "\"" + "/>")
sr.WriteLine("<add key=" + "\"" + "IsEmail" + "\"" + " value=" + "\"" + "0" + "\"" + "/>")
sr.WriteLine("<add key=" + "\"" + "IsMoveTime" + "\"" + " value=" + "\"" + "1" + "\"" + "/>")
sr.WriteLine("<add key=" + "\"" + "IsMove" + "\"" + " value=" + "\"" + "1" + "\"" + "/>")
sr.WriteLine("<add key=" + "\"" + "MoveValue" + "\"" + " value=" + "\"" + "3" + "\"" + "/>")
sr.WriteLine("</appSettings>")
sr.WriteLine("</configuration>")
sr.Close()
}
catch (Exception e)
{
sr.Close()
MessageBox.Show("创建配置文件错误!", "信息", MessageBoxButtons.OK, MessageBoxIcon.Question)
}
finally
{
if (sr != null)
{
sr.Close()
}
}
}
}
#endregion
#region 更新Config文件
/// <summary>
/// 更新Config文件
/// </summary>
/// <param name="p_strKey">键</param>
/// <param name="p_strValue">值</param>
public static void UpdateConfig(string p_strKey, string p_strValue)
{
try
{
string m_strFullPath = ""
XmlDocument xmlDoc = new XmlDocument()
m_strFullPath = Directory.GetCurrentDirectory() + @"\" + "Config.config"
ConfigFile(m_strFullPath)
if (File.Exists(m_strFullPath))
{
//处理数据文件为只读的情况
if ((File.GetAttributes(m_strFullPath) &FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
File.SetAttributes(m_strFullPath, File.GetAttributes(m_strFullPath) &(~FileAttributes.ReadOnly))
}
}
xmlDoc.Load(m_strFullPath)
XmlNodeList nodeList = xmlDoc.SelectSingleNode("/configuration/appSettings").ChildNodes
foreach (XmlNode xn in nodeList)//遍历所有子节点
{
XmlElement xe = (XmlElement)xn
if (xe.GetAttribute("key").IndexOf(p_strKey) != -1)
{
xe.SetAttribute("value", p_strValue)
}
}
xmlDoc.Save(m_strFullPath)
//fs.Close()
}
catch (System.NullReferenceException NullEx)
{
throw NullEx
}
catch (Exception ex)
{
throw ex
}
}
#endregion
}
public class ConfigInfo
{
private string strNameColor =ColorTranslator.ToHtml( System.Drawing.Color.Blue)
private string strInfoColor = ColorTranslator.ToHtml(System.Drawing.Color.Black)
private string strIsURL = "1"
private string strIsEmail = "1"
private string strIsMove = "1"
private string strMoveValue = "3"
private string strIsMoveTime = "1"
/// <summary>
/// 是否移除空格
/// </summary>
public bool IsMoveTime
{
get
{
if (strIsMoveTime == "1")
{
return true
}
else
{
return false
}
}
set
{
if (value)
{
strIsMoveTime = "1"
}
else
{
strIsMoveTime = "0"
}
}
}
/// <summary>
/// 缩进空格
/// </summary>
public decimal MoveValue
{
get
{
return Convert.ToDecimal(strMoveValue)
}
set
{
strMoveValue = value.ToString()
}
}
/// <summary>
/// 是否缩进
/// </summary>
public bool IsMove
{
get
{
if (strIsMove == "1")
{
return true
}
else
{
return false
}
}
set
{
if (value)
{
strIsMove = "1"
}
else
{
strIsMove = "0"
}
}
}
/// <summary>
/// 邮件是否可以点击
/// </summary>
public bool IsEmail
{
get
{
if (strIsEmail == "1")
{
return true
}
else
{
return false
}
}
set
{
if (value)
{
strIsEmail = "1"
}
else
{
strIsEmail = "0"
}
}
}
/// <summary>
/// 网页是否可以点击
/// </summary>
public bool IsURL
{
get
{
if (strIsURL== "1")
{
return true
}
else
{
return false
}
}
set
{
if (value)
{
strIsURL = "1"
}
else
{
strIsURL = "0"
}
}
}
/// <summary>
/// 消息颜色
/// </summary>
public Color InfoColor
{
get
{
return ColorTranslator.FromHtml(strInfoColor)
}
set
{
strInfoColor = ColorTranslator.ToHtml(value)
}
}
/// <summary>
/// 信息颜色
/// </summary>
public Color NameColor
{
get
{
return ColorTranslator.FromHtml(strNameColor)
}
set
{
strNameColor = ColorTranslator.ToHtml(value)
}
}
public ConfigInfo()
{
string m_strFullPath = ""
XmlDocument xmlDoc = new XmlDocument()
m_strFullPath = Directory.GetCurrentDirectory() + @"\" + "Config.config"
Config.ConfigFile(m_strFullPath)
xmlDoc.Load(m_strFullPath)
XmlNodeList nodeList = xmlDoc.SelectSingleNode("/configuration/appSettings").ChildNodes
foreach (XmlNode xn in nodeList)//遍历所有子节点
{
XmlNodeReader objNdRd = new XmlNodeReader(xn)
while (objNdRd.Read())
{
if (objNdRd.GetAttribute("key").IndexOf("NameColour") != -1)
{
this.strNameColor = objNdRd.GetAttribute("value")
}
if (objNdRd.GetAttribute("key").IndexOf("InfoColour") != -1)
{
this.strInfoColor = objNdRd.GetAttribute("value")
}
if (objNdRd.GetAttribute("key").IndexOf("IsURL") != -1)
{
this.strIsURL = objNdRd.GetAttribute("value")
}
if (objNdRd.GetAttribute("key").IndexOf("IsEmail") != -1)
{
this.strIsEmail = objNdRd.GetAttribute("value")
}
if (objNdRd.GetAttribute("key").IndexOf("IsMove") != -1)
{
this.strIsMove = objNdRd.GetAttribute("value")
}
if (objNdRd.GetAttribute("key").IndexOf("MoveValue") != -1)
{
this.strMoveValue = objNdRd.GetAttribute("value")
}
}
}
}
public void Update()
{
Config.DelConfig()
string m_strFullPath = ""
XmlDocument xmlDoc = new XmlDocument()
m_strFullPath = Directory.GetCurrentDirectory() + @"\" + "Config.config"
Config.ConfigFile(m_strFullPath)
Config.UpdateConfig("NameColour", strNameColor)
Config.UpdateConfig("InfoColour", strInfoColor)
Config.UpdateConfig("IsURL", strIsURL)
Config.UpdateConfig("IsEmail", strIsEmail)
Config.UpdateConfig("IsMove", strIsMove)
Config.UpdateConfig("MoveValue", strMoveValue)
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)