# XML与JSON

XML和JSON都是数据语言,以规定的格式来存储数据。C# .Net应用都使用XML以某种形式来存储数据,比如配置文件,XAML文件主要用在WPF和Windows Store应用的用户界面描述上。本章重点介绍XML,简单介绍XAML。

# XML基础

XML(Extensible Markup Language)可扩展标记语言是一种数据语言,它将数据以一种简单的文本格式存储,它是一种W3C标准格式,类似于HTML。它的细节很复杂,但大多数任务不需要了解XML的详细知识。

元素和属性是XML的两大组成要素之一,元素是<elementName>,属性则是<elementName prop="propValue">。

# XML模式

XML文档可用XML模式来描述,XML模式也是一个XML文档,但它描述的是一个XML文档的结构,即元素和属性,可以根据XML模式来验证XML文档,在C#中XML模式格式是XSD(XML Schema Definition)。

# XML文档模型

XML文档对象模型(Document Object Model,DOM)是能使程序有能力动态的访问和更新文档的内容,结构的接口。构成DOM的类在System.Xml名称空间中,它常见的几个类如下:

类名 说明
XmlNode 表示文档树的一个节点,它是很多类的基类,如果它是文档的根节点,可以从它导航到文档任意位置
XMLDocument 扩展了XmlNode,通常是使用XML的第一个对象,用于加载磁盘或其他地方的数据,并保存数据
XmlElement 表示XML文档中的一个元素,派生于XmlLinkedNode,XmlLinkedNode派生于XmlNode
XmlAttribute 表示一个特性,跟XMLDocument类一样,它也派生于XmlNode类
XmlText 表示开始标记和结束标记之间的文本
XmlComment 表示一种特殊的节点,这个节点不是文档的一部分,但为阅读器提供文档的各部分信息
XmlNodeList 表示一个节点集合

下面我们用程序来获取并分析一个XML文件结构:

static void Main(string[] args)
{
    XmlDocument document = new XmlDocument();
    document.Load(@"Test.xml");
    XmlElement element = document.DocumentElement;
    GetALLXmlStructure(element);

}

static void GetALLXmlStructure(XmlElement element)
{
    Console.WriteLine($"current node Name:{element.Name}!--------");
    Console.WriteLine($"current node InnerXml:{element.InnerXml}");
    Console.WriteLine($"current node InnerText:{element.InnerText}");
    Console.WriteLine($"current node type:{element.NodeType}");
    Console.WriteLine($"current node value:{element.Value}");

    void GetChildNode(XmlNodeList xmlNodeList, int level = 0)
    {
        level++;
        string tempIntentStr = new string(' ', level*4);
        foreach(XmlNode xmlOne in xmlNodeList)
        {
            Console.WriteLine(tempIntentStr + $"current node Name:{xmlOne.Name}!--------");
            //Console.WriteLine(tempIntentStr + $"current node InnerXml:{xmlOne.InnerXml}");
            //Console.WriteLine(tempIntentStr + $"current node InnerText:{xmlOne.InnerText}");
            Console.WriteLine(tempIntentStr + $"current node type:{xmlOne.NodeType}");
            Console.WriteLine(tempIntentStr + $"current node value:{xmlOne.Value}");
            if (xmlOne.Attributes != null)
            {
                foreach(XmlAttribute oneAttr in xmlOne.Attributes)
                {
                    Console.WriteLine(tempIntentStr+$"have attributes:{oneAttr.Name},value:{oneAttr.Value}");
                }
            }
            if (xmlOne.HasChildNodes)
            {
                GetChildNode(xmlOne.ChildNodes,level);
            }
        }
    }

    if (element.HasChildNodes)
    {
        GetChildNode(element.ChildNodes);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

通过上面的分析发现:

  • InnerText获取当前节点中所有子节点的文本,把它作为一个串联字符串返回。
  • InnerXml属性返回包括xml标记的文本。
  • value能有返回值得类只有XmlText,XmlComment,XmlAttribute。

然后我们根据上面的分析来写一个输出xml文件的输出程序:

static void Main(string[] args)
{
    XmlDocument document = new XmlDocument();
    document.Load(@"Test.xml");
    XmlElement element = document.DocumentElement;
    Console.WriteLine(GetAllXmlFileContent(element));

}

static string GetAllXmlFileContent(XmlElement element)
{
    string returnCode = "\r\n";
    string resultStr = "";

    void GetXmlContent(XmlNode xmlNode, int level = -1)
    {
        level++;
        string tempIntentStr = new string(' ', level*2);
        if(xmlNode is XmlComment)
        {
            resultStr += tempIntentStr + $"<!--{xmlNode.Value}-->{returnCode}";
            return;
        }
        resultStr += tempIntentStr+"<"+xmlNode.Name;
        //分析是否有属性
        if (xmlNode.Attributes != null)
        {
            foreach (XmlAttribute oneAttr in xmlNode.Attributes)
            {
                resultStr += $" {oneAttr.Name}=\"{oneAttr.Value}\"";
            }
        }
        resultStr += ">";
        if (xmlNode.HasChildNodes)
        {
            if(xmlNode.ChildNodes.Count == 1 && xmlNode.ChildNodes[0] is XmlText) {
                resultStr += xmlNode.ChildNodes[0].Value+$"</{xmlNode.Name}>{returnCode}";
            }
            else
            {
                resultStr += returnCode;
                foreach (XmlNode xmlOne in xmlNode.ChildNodes)
                {
                    //Console.WriteLine("enter node:" + xmlOne.Name);
                    GetXmlContent(xmlOne,level);
                }
                resultStr += tempIntentStr+$"</{xmlNode.Name}>{returnCode}";
            }
        }
    }

    GetXmlContent(element);
    return resultStr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

下面是对DOM节点的一些增删改查:

static void Main(string[] args)
{
    XmlDocument document = new XmlDocument();
    document.Load(@"Test.xml");
    XmlElement element = document.DocumentElement;
    Console.WriteLine("--------------原文本内容:-------------------");
    Console.WriteLine(GetAllXmlFileContent(element));
    Console.WriteLine("--------------得到某个节点的值-------------");
    Console.WriteLine("返回当前节点之后的第一个子节点");
    Console.WriteLine(element.ChildNodes[0].FirstChild.InnerXml);
    Console.WriteLine("返回当前节点之后的最后一个子节点");
    Console.WriteLine(element.ChildNodes[0].LastChild.InnerXml);
    Console.WriteLine("返回当前节点的父节点");
    Console.WriteLine(element.ChildNodes[0].LastChild.ParentNode.InnerXml);
    Console.WriteLine("返回当前节点有相同父节点的下一个兄弟节点");
    Console.WriteLine(element.ChildNodes[0].NextSibling.InnerXml);
    Console.WriteLine("-----------------修改第一个子节点的值后----------------");
    element.ChildNodes[0].FirstChild.FirstChild.Value = "Ceshi";
    Console.WriteLine(element.ChildNodes[0].FirstChild.InnerXml);
    Console.WriteLine("---------------删除第二个子节点--------------------");
    XmlNode deleteNode = element.RemoveChild(element.ChildNodes[1]);
    Console.WriteLine(GetAllXmlFileContent(element));
    Console.WriteLine("---------------将删除的子节点再插入原文档--------------");
    element.AppendChild(deleteNode);
    Console.WriteLine(GetAllXmlFileContent(element));
    Console.WriteLine("---------------创建新的节点并插入----------------------");
    XmlElement newNode = document.CreateElement("student");
    newNode.SetAttribute("status", "ready");
    XmlElement nameNode = document.CreateElement("name");
    nameNode.AppendChild(document.CreateTextNode("This is person info"));
    newNode.AppendChild(nameNode);
    newNode.InsertBefore(document.CreateComment("测试注释"),newNode.ChildNodes[0]);
    element.InsertAfter(newNode, element.ChildNodes[1]);
    Console.WriteLine(GetAllXmlFileContent(element));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# XPath搜索XML

XPath的示例操作如下:

static void Main(string[] args)
{
    XmlDocument document = new XmlDocument();
    document.Load(@"Test.xml");
    XmlNode xmlNode1 = document.SelectSingleNode("//person[@p='123']");
    Console.WriteLine(xmlNode1.InnerXml);

    Console.WriteLine("----------------select person node--------------");
    XmlNodeList xnl = document.SelectNodes("//person");
    if(xnl.Count>0)
        foreach (XmlNode one in xnl){
            Console.WriteLine(one.InnerXml);
        }

    Console.WriteLine("select node");
    Console.WriteLine(document.SelectSingleNode("//person[age='22']").InnerXml);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# JSON基础

JSON是JavaScript Object Notation,它比XML要简洁的多,它是web传输过程中最常用的轻量级的文本数据交换格式。JavaScript能够使用内置的eval()函数用JSON数据来生成原生的JavaScript对象。它源于JavaScript但是独立于语言和平台,很多语言都支持这种格式。

# XML转换成JSON

可以通过NuGet Package Manager安装一个Newtonsoft JSON.NET包:

static void Main(string[] args)
{
    XmlDocument document = new XmlDocument();
    document.Load(@"Test.xml");
    string json = Newtonsoft.Json.JsonConvert.SerializeXmlNode(document);
    Console.WriteLine(json);
}
1
2
3
4
5
6
7