C#学习之Linq to Xml
前言
我相信很多从事.NET开发的,在.NET 3.5之前操作XML会比较麻烦,但是在此之后出现了Linq to Xml,而今天的主人公就是Linq to Xml,废话不多说,直接进入主题。
题外:最近由于身体原因去医院,耽误了不少时间,不然这篇随笔可能早就完成了。
目录:
一、生成Xml
为了能够在结构有一定的组织,笔者建议大家新建一个控制台项目,并且新建一个CreateXml类(以下部分都属于该类中)。
并在其中写入以下属性:
public static String Path
{
get
{
String path = String.Format("{0}\\test.xml", Environment.CurrentDirectory);
return path;
}
}
这句代码很好理解,就是为了下面我们示例的时候可以将xml保存到当前程序的运行路径下。
(以下的示例中不会包含Main方法中的写法,因为Main中仅仅只要调用该静态方法即可。)
1.创建简单的Xml
首先我们先练练手,创建一个简单的Xml并保存到一个文件中。
代码如下:
/// <summary>
/// 创建简单的xml并保存
/// </summary>
public static void CreateElement()
{
XDocument xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("root",
new XElement("item", ""),
new XElement("item", "")
));
xdoc.Save(Path);
}
很多学习过XML的人可以从结构就能够猜测出最终的xml的组织,而这也是linq to xml的优点之一。这句代码首先创建一个xml文档,并设置该xml的版本为1.0,
采用utf-8编码,后面的yes表示该xml是独立的。下面就开始创建每个节点的,首先是Root节点,然后在Root节点中添加两个Item节点。
最终生成的Xml如下所示:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<root>
<item>1</item>
<item>2</item>
</root>
2.创建注释
当xml有很多项时,我们就需要利用注释加以区别,通过linq to xml我们一样可以在其中添加注释。
比如下面这段代码:
/// <summary>
/// 创建注释
/// </summary>
public static void CreateComment()
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("提示"),
new XElement("item", "asd")
);
doc.Save(Path);
}
这里我们直接在版本信息的后面添加了一条注释。
最终的结果如下所示:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--提示-->
<item>asd</item>
3.根据对象创建xml
很多时候我们都会将数组之类的类型转换成xml以便保存进永久性存储介质中,所以下面我们也简单的举了一个例子,将数组转换成xml。
代码如下所示:
/// <summary>
/// 根据对象创建xml并保存
/// </summary>
public static void CreateElementByObjects()
{
var s = Enumerable.Range(, );
XElement xele = new XElement(
"Root",
from item in s
select new XElement("item", item.ToString())
);
xele.Save(Path);
}
一开始的代码 var s = Enumerable.Radge(1,10)是从1开始递增,生成含有10项的数组,以便后面我们进行添加,有了这个数组之后,
我们通过简单的linq语句将数组转换成xml,添加到Root中。
保存之后的结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
</Root>
4.创建属性
有时我们不想创建新的子项去保存数据,而是使用属性的方式去保存。理所应当,linq to xml一样也支持这个功能,下面我们可以通过简单的语句去实现它。
代码如下所示:
/// <summary>
/// 创建属性
/// </summary>
public static void CreteAttribute()
{
XAttribute xa = new XAttribute("V2", "");
XElement xele = new XElement(
"Root",
new XElement("Item",
new XAttribute("V1", ""),
xa
));
xele.Save(Path);
}
我们依然可以看到熟悉的语法,这里我们利用了XAttribute去创建一个属性,并添加到XElement中。
最终的结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item V1="1" V2="2" />
</Root>
5.创建命名空间
对于一些企业级的xml格式,会非常的严格。特别是在同一个xml中可能会出现重复的项,但是我们又想区分开来,这个时候我们可以利用命名空间将他们分开(跟C#中的命名空间类似。)。
下面是创建命名空间的示例:
/// <summary>
/// 创建命名空间
/// </summary>
public static void CreateNamespace()
{
XElement xele = new XElement("{http://www.xamarin-cn.com}Root",
new XElement("Item", ""),
new XElement("{http://www.baidu.com}Item", ));
xele.Save(Path);
}
结果如下所示:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns="http://www.xamarin-cn.com">
<Item xmlns="">1</Item>
<Item xmlns="http://www.baidu.com">2</Item>
</Root>
从这个结果中我们可以看到对应的属性中有了xmlns属性,并且值就是我们赋给它的命名空间。
二、查询并修改Xml
Linq to xml不仅仅是创建xml简单,在查询,编辑和删除方面一样是非常方便的。下面我们就会介绍这些。
首先我们创建一个QueryXml类,并在其中写入如下的属性:
public static String Path
{
get
{
String path = String.Format("{0}\\test1.xml", Environment.CurrentDirectory);
return path;
}
}
同时在该路径下新建一个test1.xml文件,并在其中写入如下内容:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item v1="1" v2="2">Item1</Item>
<Item v1="1" v2="2" >Item2</Item>
</Root>
下面我们就可以正式开始了。
1.通过文件读取xml
既然我们要对xml查询就需要读取对应的xml文件,当然后面会介绍其他的方式。
代码如下:
/// <summary>
/// 通过文件读取xml
/// </summary>
public static void QueryElementByFile()
{
XElement xele = XElement.Load(Path);
XElement xele1 = xele.Element("Item");
Console.Write(xele1.Value.Trim());
Console.ReadKey();
}
我们可以利用XElement的静态方法Load读取指定路径下的xml文件,这里我们不仅读取了该xml文件,同时还获取的该xml的第一个item的值并输出。
所以我们可以看到如下的结果:

2.在指定节点前后添加新节点
上面我们仅仅只是读取xml以及简单的查询,下面我们不仅仅查询并且还要在该节点前后插入新的节点。
代码如下:
/// <summary>
/// 在指定节点前后添加新节点
/// </summary>
public static void AddToElementAfterAndBefore()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Elements("Item")
where ele.Value.Equals("Item2")
select ele).SingleOrDefault();
if (item != null)
{
XElement nele = new XElement("NItem", "NItem");
XElement nele2 = new XElement("BItem", "BItem");
item.AddAfterSelf(nele);
item.AddBeforeSelf(nele2);
xele.Save(Path);
}
}
我们简单的分析一下上面的代码,首先我们利用linq从中查询Item的值为Item2的节点,然后获取其中第一个节点,然后通过AddAfterSelf和AddBeforeSelf在该节点的后面和前面分别添加新的节点。
添加完之后的xml结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item v1="1" v2="2">Item1</Item>
<BItem>BItem</BItem>
<Item v1="1" v2="2">Item2</Item>
<NItem>NItem</NItem>
</Root>
3.添加属性到节点中
我们已经可以动态的添加节点,但是创建的时候不仅仅可以创建节点,并且还能创建属性,下面我们可以通过SetAttributeValue去添加新的属性或者修改现有属性。
代码如下:
/// <summary>
/// 添加属性到节点中
/// </summary>
public static void AddAttributeToEle()
{
XElement xele = XElement.Parse(@"<?xml version='1.0' encoding='utf-8'?><Root><!--前面的注释-->
<Item v1='1' v2='2'>Item1</Item><!--后面的注释--><Item v1='1' v2='2' v3='3'>Item2</Item></Root>");
var item = (from ele in xele.Elements("Item")
where ele.Value.Equals("Item2")
select ele).SingleOrDefault();
item.SetAttributeValue("v3", "");
xele.Save(Path);
}
我们可以明显的看出,这里我们已经不是使用XElement.Load去读取xml文件,而是通过直接读取xml字符串。接着我们还是跟上面一样去查询,然后通过SetAttributeValue添加了新的属性,并保存。
Xml内容如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!--前面的注释-->
<Item v1="1" v2="2">Item1</Item>
<!--后面的注释-->
<Item v1="1" v2="2" v3="3">Item2</Item>
</Root>
我们可以看到第二个Item中多了一个 v3=”3” 新的属性。
4.添加注释到指定节点前后
这里的语法基本跟添加节点到指定节点前后是相似的,只是读取xml的方式不同。
代码如下:
/// <summary>
/// 添加注释到节点前后
/// </summary>
public static void AddCommentToAfterAndBefore()
{
TextReader tr = new StringReader(@"<?xml version='1.0' encoding='utf-8'?><Root><!--前面的注释-->
<Item v1='1' v2='2'>Item1</Item><!--后面的注释--><Item v1='1' v2='2' v3='3'>Item2</Item></Root>");
XElement xele = XElement.Load(tr);
var item = (from ele in xele.Elements("Item")
where ele.Value.Equals("Item1")
select ele).FirstOrDefault();
if (item != null)
{
XComment xcom = new XComment("后面的注释");
XComment xcoma = new XComment("前面的注释");
item.AddAfterSelf(xcom);
item.AddBeforeSelf(xcoma);
}
tr.Close();
xele.Save(Path);
}
上面我使用StringReader和TextReader读取xml字符串并使用XElement.Load读取该对象,然后就是在新建节点的时候新建的是注释节点,最后利用一样的语法添加到指定节点前后。
最终结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!--前面的注释-->
<!--前面的注释-->
<Item v1="1" v2="2">Item1</Item>
<!--后面的注释-->
<!--后面的注释-->
<Item v1="1" v2="2" v3="3">Item2</Item>
</Root>
5.替换指定节点
修改节点的值通过SetValue即可做到,但是有时涉及到子节点,而我们想一次性全部替换掉,那么我们就需要使用ReplaceWith。
代码如下:
/// <summary>
/// 替换指定节点
/// </summary>
public static void ReplaceElement()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Elements("Item")
where ele.Value.Equals("Item2")
select ele).FirstOrDefault();
if (item != null)
{
item.ReplaceWith(new XElement("Item", "Item3"));
}
xele.Save(Path);
}
这里的重点在于ReplaceWith方法,调用该方法会发生两个操作。首先是删除该节点,然后在该节点的位置上将我们的节点插入完成替换。
最后的xml结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!--前面的注释-->
<!--前面的注释-->
<Item v1="1" v2="2">Item1</Item>
<!--后面的注释-->
<!--后面的注释-->
<Item>Item3</Item>
</Root>
这样我们很轻易的就替换了整个节点。
6.删除指定属性
前面我们介绍了创建、修改和添加属性,但是还没有介绍如何删除指定的属性,下面我们就通过一个简单的实例来演示。
代码如下:
/// <summary>
/// 删除指定属性
/// </summary>
public static void RemoveAttribute()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Elements("Item")
where ele.Value.Equals("Item1")
select ele).FirstOrDefault().Attribute("v1");
if (item != null)
{
item.Remove();
}
xele.Save(Path);
}
我们首先查询出指定的节点,然后指定某个属性,最后调用XAttribute的Remove方法既可。
结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!--前面的注释-->
<!--前面的注释-->
<Item v2="2">Item1</Item>
<!--后面的注释-->
<!--后面的注释-->
<Item>Item3</Item>
</Root>
7.删除指定节点
既然上面已经可以删除属性,自然也少不了删除属性。
代码如下所示:
/// <summary>
/// 删除指定节点
/// </summary>
public static void RemoveElement()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Elements("Item")
where ele.Value.Equals("Item1")
select ele).FirstOrDefault();
if (item != null)
{
item.Remove();
}
xele.Save(Path);
}
依然是调用同样的方法。
结果如下:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!--前面的注释-->
<!--前面的注释-->
<!--后面的注释-->
<!--后面的注释-->
<Item>Item3</Item>
</Root>
三、按节点关系查询
上面的查询都是通过相关的条件进行查询,但是我们有时仅仅只需要通过之间的关系即可,这样反而可以避免很多的代码,当然稍加探索可以发现其实XElement都提供给我们了。
我们依然要新建一个StructureXml类,并在其中新建一个属性。
如下所示:
public static String Path
{
get
{
String path = String.Format("{0}\\test2.xml", Environment.CurrentDirectory);
return path;
}
}
同时在该文件夹下新建一个test2.xml并写入如下内容:
<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Item>
<SubItem1>
1
</SubItem1>
<SubItem>
<Child>
sss
</Child>
</SubItem>
<SubItem2>
2
</SubItem2>
</Item>
</Root>
1.显示指定节点的所有父节点
通过上面的xml文件,我们清晰的看出xml是具有结构性的,彼此之间都存在关系,而现在我们需要显示某个节点的父级节点的名称。
代码如下所示:
/// <summary>
/// 显示指定节点的所有父节点
/// </summary>
public static void ShowAllParentEle()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Descendants("Child")
select ele).FirstOrDefault();
if (item != null)
{
foreach (var sub in item.Ancestors())
{
Console.WriteLine(sub.Name);
}
Console.WriteLine("----------------");
foreach (var sub in item.AncestorsAndSelf())
{
Console.WriteLine(sub.Name);
}
Console.ReadKey();
}
}
其中我们通过Descendants获取最底的节点,然后使用Ancestors获取所有的父级节点,而AncestorsAndSelf则表示包含本身。
最终结果如下所示:

我们从图中看出,分割线前显示的是不包含本身的,而下面是包含本身的。
2.显示指定节点的所有子节点
我们不仅仅可以输出一个节点的所有父级节点,同样也可以输出一个节点的所有子节点。
代码如下所示:
/// <summary>
/// 显示指定节点的所有子节点
/// </summary>
public static void ShowAllChildEle()
{
XElement xele = XElement.Load(Path);
foreach (var sub in xele.Descendants())
{
Console.WriteLine(sub.Name);
}
Console.WriteLine("-----------------");
foreach (var sub in xele.DescendantsAndSelf())
{
Console.WriteLine(sub.Name);
}
Console.ReadKey();
}
这里我们依然是分成输出子级节点以及包含自己的。
结果如下所示:

3.显示同级节点之前的节点
既然有了父子关系,当然也少不了同级关系,首先我们先显示同级节点之前的节点。
代码如下所示:
/// <summary>
/// 显示同级节点之前的节点
/// </summary>
public static void ShowPrevEle()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Descendants("SubItem")
select ele).FirstOrDefault();
if (item != null)
{
foreach (var sub in item.ElementsBeforeSelf())
{
Console.WriteLine(sub.Name);
}
}
Console.ReadKey();
}
这里我们看到我们通过ElementsBeforeSelf获取该节点之前的同级节点,当然我们还可以传入参数作为限制条件。这里我们通过查询获取了SubItem这个节点,并显示该节点之前的同级节点。
最终结果如下:

4.显示同级节点后面的节点
作为上面的补充。
代码如下所示:
/// <summary>
/// 显示同级节点后面的节点
/// </summary>
public static void ShowNextEle()
{
XElement xele = XElement.Load(Path);
var item = (from ele in xele.Descendants("SubItem")
select ele).FirstOrDefault();
if (item != null)
{
foreach (var sub in item.ElementsAfterSelf())
{
Console.WriteLine(sub.Name);
}
}
Console.ReadKey();
}
最终结果如下所示:

四、监听xml事件
你可能会疑惑xml为什么还要监听,其实这样是有意义的,比如你要根据某个节点的值作为依赖,那么你就要监听这个节点,如果这个节点发生改变的时候,
你才可以及时的作出反应。但是xml的事件监听有一个特点,跟浏览器中的DOM事件类似,监听父节点同样也可以监听的到它的子节点的事件。下面我们
通过一个简单的实例来说明。
实例代码如下:
public static class EventXml
{
public static void BindChangeing()
{
XElement xele = new XElement("Root");
xele.Changing += xele_Changing;
xele.Changed += xele_Changed;
xele.Add(new XElement("Item", ""));
var item = xele.Element("Item");
item.ReplaceWith(new XElement("Item", ""));
item = xele.Element("Item");
item.Remove();
Console.ReadKey();
} static void xele_Changed(object sender, XObjectChangeEventArgs e)
{
XElement ele = sender as XElement;
Console.WriteLine(String.Format("已完成 {0}-{1}", ele.Name, e.ObjectChange));
} static void xele_Changing(object sender, XObjectChangeEventArgs e)
{
XElement ele = sender as XElement;
Console.WriteLine(String.Format("正在进行中 {0}-{1}", ele.Name, e.ObjectChange));
}
}
其中的关键就是Changing和Changed事件,其次就是在事件中判断事件的来源。
最终结果如下所示:

五、处理xml流
在实际的商业化的开发中,xml不可能仅仅保存这么点数据。有可能保存着非常多的数据。但是我们还是按照以往的方式,就会将xml全部读取进内存。
这样会占据很多内存,影响系统的性能,针对这种情况我们需要使用流的方式去处理xml,因为流会按照我们的顺序读取部分xml进内存,并不会将所
有xml都读取进内存。
Xml文件内容如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<Root>
<SubItem>1</SubItem>
<SubItem>1</SubItem>
<SubItem>1</SubItem>
<Item>A</Item>
<SubItem>1</SubItem>
<Item>B</Item>
</Root>
代码如下所示:
public static class ReadXmlStream
{
public static String Path
{
get
{
String path = String.Format("{0}\\test3.xml", Environment.CurrentDirectory);
return path;
}
} /// <summary>
/// 流式处理XML
/// </summary>
public static void ReadXml()
{
XmlReader reader = XmlReader.Create(Path);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name.Equals("Item"))
{
XElement ele = XElement.ReadFrom(reader) as XElement;
Console.WriteLine(ele.Value.Trim());
}
}
Console.ReadKey();
}
}
这里我们通过XmlReader的Create静态方法打开xml文件,并通过Read一个节点的进行读取,并判断该节点的类型。
最终结果如下:

C#学习之Linq to Xml的更多相关文章
- Linq学习<四> linq to XML
		LINQ to XML并不打算替代标准的XML API,例如,XML DOM(Document Object Model).Path.XQuery和XSLT等.如果熟悉这些API或当前需要使用或学习它 ... 
- XML基础学习02<linq to xml>
		Linq to XML的理解 1:这是一种比较好的操作Xml的工具. àXDocument 文档 àXElement 元素 àXAttribute 属性 àXText 文本 2:这里还是和我们之前创建 ... 
- Linq学习笔记---Linq to Xml操作
		LINQ to XML的成员, 属性列表: 属性 说明 Document 获取此 XObject 的 XDocument EmptySequence 获取空的元素集合 FirstAttribut ... 
- LINQ to XML LINQ学习第一篇
		LINQ to XML LINQ学习第一篇 1.LINQ to XML类 以下的代码演示了如何使用LINQ to XML来快速创建一个xml: public static void CreateDoc ... 
- [开发笔记]-Linq to xml学习笔记
		最近需要用到操作xml文档的方法,学习了一下linq to xml,特此记录. 测试代码: class Program { //参考: LINQ to XML 编程基础 - luckdv - 博客园 ... 
- Linq学习<五> 运用linq查询Xml
		这节将学习如何用 linq查询xml 一.我们先看看在xml中我们怎么操作 public void xmlWayToQueryXmlFile() { XmlDocument xmldoc = new ... 
- Linq学习总结2--Linq to XML
		概述: linq to xml(下面简称ltx好了),是微软根据linq技术对于XML的CURD.使用起来比System.XML中的XML操作方式更加简便.这段时间使用它在公司里升级了老板的邮件系统, ... 
- 不挣扎了,开始学习LINQ TO XML,进而来解析网页。
		找到了别人遇到和我一样的问题:http://ylad.codeplex.com/discussions/430095(英文) 一位叫做Mister Goodcat的提供了信息: Short answe ... 
- linq to xml学习
		http://www.cnblogs.com/greatverve/archive/2010/07/09/linq-to-xml-add-delete-update-query.html 记录一下,别 ... 
随机推荐
- JAVA编程规则【转自java编程思想】
			本附录包含了大量有用的建议,帮助大家进行低级程序设计,并提供了代码编写的一般性指导: (1) 类名首字母应该大写.字段.方法以及对象(句柄)的首字母应小写.对于所有标识符,其中包含的所有单词都应紧靠在 ... 
- java笔记1
			第五天学习笔记 1.面对对象的理解并举例? 面对对象的核心:找合适的对象做合适的事情. 面对对象编程的思想:尽可能的用计算机语言来描述现实生活中的事物. 面对对象:侧重于对象 2.类与对象之间的关系? ... 
- 一步步编写avalon组件01:弹出层组件
			avalon2已经稳定下来,是时候教大家如何使用组件这个高级功能了. 组件是我们实现叠积木开发的关键. avalon2实现一个组件非常轻松,并且如何操作这个组件也比以前的avalon2,还是react ... 
- [Reship]如何回复审稿人意见
			================================= This article came from here:http://blog.renren.com/GetEntry.do?id= ... 
- tdd  和 make file,以及cygwin
			等我把这本书看完,好好总结一下. 还要把以前的博客文字整理一下 
- DIOCP之EchoServer分析
			constructor TfrmMain.Create(AOwner: TComponent);begin inherited Create(AOwner); sfLogger.setAppender ... 
- Android permission
			1. users-permission Users-permission is the permission that this app should acquire, so that the app ... 
- python 基础理解...
			class obj(object): def __getattribute__(self, *args, **kwargs): # 访问属性就会被调用 print("__getattribu ... 
- 反射类属性生成DataTable
			public class People //类名 { private static string name; //字段 private string sex;//字段 public string Se ... 
- html   css 注释
			html <!-- 注释内容 --> css注释/* 注释内容*/ 
