C#中如何读取XML文件


目录


前言

读者在阅读《游戏设计、原型与开发》中第四个实例原型《Prospector》的过程中,看到源代码使用的是自己创建的PT_XMLReader脚本,名字应该表示“Prototool_XML Reader”的意思。在使用该脚本读取XML文件时,稍有繁琐,需要考虑XML文件某个节点的属性是否存在等问题。我在跟着教程写代码时,写了使用C#里的API读取XML文件的版本。在此先给出这一部分代码以供参考。


#region 从XML文件中获取Sprite的位置信息——C#
/* ---读取XML文件--- */

// Resources文件夹比较特殊,不能使用相对路径访问
// 注意,无需为文件添加后缀
TextAsset xmlAsset = Resources.Load("XML Versions of Deck & Layout/XML_Deck");

// 检查是否获取到文件
if (xmlAsset == null)
{
    Debug.Log("XML_Deck文件获取失败!");
    return;
}
else
{
    Debug.Log("XML_Deck文件获取成功!");
}

XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlAsset.text);

/* 读---取XML文件中的 decorator节点--- */
// 在 decorator前添加 // 表示是在文件全局中寻找
XmlNodeList nodesDeco = doc.SelectNodes("//decorator");
Debug.Log($"nodesDeco.Count:{nodesDeco.Count}");

decorators = new List();
Decorator deco;

if (nodesDeco.Count > 0)
{
    // 读取并设置decorator
    foreach (XmlNode n in nodesDeco)
    {
        deco = new Decorator();
        // 添加?作为可空类型,可以避免XML中没写这个属性,但是却使用了其Value,导致空引用报错
        deco.type = n.Attributes["type"]?.Value;
        deco.loc.x = Convert.ToSingle(n.Attributes["x"]?.Value);
        deco.loc.y = Convert.ToSingle(n.Attributes["y"]?.Value);
        deco.loc.z = Convert.ToSingle(n.Attributes["z"]?.Value);
        deco.flip = n.Attributes["flip"]?.Value == "1";
        deco.scale = Convert.ToSingle(n.Attributes["scale"]?.Value);

        decorators.Add(deco);
    }
}

/* ---读取XML文件中的 card 节点--- */
XmlNodeList nodesCard = doc.SelectNodes("//card");

cardDefs = new List();
CardDefinition cDef;

if (nodesCard.Count > 0)
{
    foreach (XmlNode n in nodesCard)
    {
        cDef = new CardDefinition();

        cDef.rank = int.Parse(n.Attributes["rank"]?.Value);

        XmlNodeList childNodesCard = n.ChildNodes;

        foreach (XmlNode childN in childNodesCard)
        {
            deco = new Decorator();

            deco.type = "pip";

            deco.flip = childN.Attributes["flip"]?.Value == "1";
            deco.loc.x = Convert.ToSingle(childN.Attributes["x"]?.Value);
            deco.loc.y = Convert.ToSingle(childN.Attributes["y"]?.Value);
            deco.loc.z = Convert.ToSingle(childN.Attributes["z"]?.Value);
            deco.scale = Convert.ToSingle(childN.Attributes["scale"]?.Value);

            cDef.pips.Add(deco);
        }

        // J、Q、K卡面是人脸
        cDef.face = n.Attributes["face"]?.Value;
        cardDefs.Add(cDef);

    }
}
#endregion
          

以下是我上面代码读取的Xml文件


<xml>
	
	<decorator type="letter" x="-1.05" y="1.42"  z="0" flip="0" scale="1.25"/>
	<decorator type="suit"   x="-1.05" y="1.03"  z="0" flip="0" scale="0.4" />
	<decorator type="suit"   x="1.05"  y="-1.03" z="0" flip="1" scale="0.4" />
	<decorator type="letter" x="1.05"  y="-1.42" z="0" flip="1" scale="1.25"/>
	
	<card rank="1">
		<pip x="0" y="0" z="0" flip="0" scale="2"/>
	</card>
	<card rank="2">
		<pip x="0" y="1.1" z="0" flip="0"/>
		<pip x="0" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="3">
		<pip x="0" y="1.1" z="0" flip="0"/>
		<pip x="0" y="0" z="0" flip="0"/>
		<pip x="0" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="4">
		<pip x="-.55" y="1.1" z="0" flip="0"/>
		<pip x=".55" y="1.1" z="0" flip="0"/>
		<pip x="-.55" y="-1.1" z="0" flip="1"/>
		<pip x=".55" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="5">
		<pip x="-.55" y="1.1" z="0" flip="0"/>
		<pip x=".55" y="1.1" z="0" flip="0"/>
		<pip x="0" y="0" z="0" flip="0"/>
		<pip x="-.55" y="-1.1" z="0" flip="1"/>
		<pip x=".55" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="6">
		<pip x="-.55" y="1.1" z="0" flip="0"/>
		<pip x=".55" y="1.1" z="0" flip="0"/>
		<pip x="-.55" y="0" z="0" flip="0"/>
		<pip x=".55" y="0" z="0" flip="0"/>
		<pip x="-.55" y="-1.1" z="0" flip="1"/>
		<pip x=".55" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="7">
		<pip x="-.55" y="1.1" z="0" flip="0"/>
		<pip x=".55" y="1.1" z="0" flip="0"/>
		<pip x="0" y=".55" z="0" flip="0"/>
		<pip x="-.55" y="0" z="0" flip="0"/>
		<pip x=".55" y="0" z="0" flip="0"/>
		<pip x="-.55" y="-1.1" z="0" flip="1"/>
		<pip x=".55" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="8">
		<pip x="-.55" y="1.1" z="0" flip="0"/>
		<pip x=".55" y="1.1" z="0" flip="0"/>
		<pip x="0" y=".55" z="0" flip="0"/>
		<pip x="-.55" y="0" z="0" flip="0"/>
		<pip x=".55" y="0" z="0" flip="0"/>
	  <pip x="0" y="-.55" z="0" flip="1"/>
		<pip x="-.55" y="-1.1" z="0" flip="1"/>
		<pip x=".55" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="9">
		<pip x="-.58" y="1.1" z="0" flip="0"/>
		<pip x=".58" y="1.1" z="0" flip="0"/>
		<pip x="-.58" y=".37" z="0" flip="0"/>
		<pip x=".58" y=".37" z="0" flip="0"/>
		<pip x="0" y="0" z="0" flip="0"/>
		<pip x="-.58" y="-.37" z="0" flip="1"/>
		<pip x=".58" y="-.37" z="0" flip="1"/>
		<pip x="-.58" y="-1.1" z="0" flip="1"/>
		<pip x=".58" y="-1.1" z="0" flip="1"/>
	</card>
	<card rank="10">
		<pip x="-.58" y="1.1" z="0" flip="0"/>
		<pip x=".58" y="1.1" z="0" flip="0"/>
		<pip x="0" y=".73" z="0" flip="0"/>
		<pip x="-.58" y=".37" z="0" flip="0"/>
		<pip x=".58" y=".37" z="0" flip="0"/>
		<pip x="-.58" y="-.37" z="0" flip="1"/>
		<pip x=".58" y="-.37" z="0" flip="1"/>
		<pip x="0" y="-.73" z="0" flip="1"/>
		<pip x="-.58" y="-1.1" z="0" flip="1"/>
		<pip x=".58" y="-1.1" z="0" flip="1"/>
	</card>
	
	<card rank="11" face="FaceCard_11"/>
	<card rank="12" face="FaceCard_12"/>
	<card rank="13" face="FaceCard_13"/>
</xml>
          

一、相关API概述

开门见山!


using System.IO; // 引用该命名空间,以使用读写操作的API,在本文章中暂不说明,读者可以在互联网或者结合AI了解如何使用
using System.Xml; // 引用该命名空间,以使用操作Xml文件的API
            
XmlDocument

是一个类,表示一个Xml文件

XmlNodeList

是一个类,表示存储Xml文件中的节点的列表

XmlNode

是一个类,表示一个节点

void XmlDocument.LoadXml(string xml)

是一个成员方法,根据xml文件的名称加载Xml文件

XmlNodeList XmlNode.SelectNodes(string xpath)

是一个成员方法,根据节点名称获取所有符合条件的节点

SelectNode(string xpath)

根据节点名称找到一个符合条件的节点

再继续下去就有点像XML API介绍了,其实这些内容可以从微软官方文档中找到XmlDocument类定义-Microsoft

接下来我将介绍如何使用。


二、如何读取XML文件

步骤说明:

  1. 创建一个XDocument实例,并调用其LoadXml()方法获取Xml文件
  2. 如果需要获取某个节点,使用SelectNode(string xpath)方法;如果需要获取相同名称的所有节点,使用SelectNodes(string xpath)方法
  3. 需要获取节点的属性,使用节点的attribute["属性名"]获取。

以上面的示例代码为例,


XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlAsset.text);
          

这一步获取Xml文件。


 /* ---读取XML文件中的 decorator节点--- */
 // 在 decorator前添加 // 表示是在文件全局中寻找
 XmlNodeList nodesDeco = doc.SelectNodes("//decorator");
 Debug.Log($"nodesDeco.Count:{nodesDeco.Count}");            
          

这一步在整个Xml文件中寻找名为"decorator"的节点

注意:这里的节点名称前添加了“//”,表示会在整个Xml文件中寻找。你也可以先获取Xml中的某个根节点,然后在根节点的子节点里寻找。

 if (nodesDeco.Count > 0)
 {
     // 读取并设置decorator
     foreach (XmlNode n in nodesDeco)
     {
         deco = new Decorator();
         // 添加?作为可空类型,可以避免XML中没写这个属性,但是却使用了其Value,导致空引用报错
         deco.type = n.Attributes["type"]?.Value;
         deco.loc.x = Convert.ToSingle(n.Attributes["x"]?.Value);
         deco.loc.y = Convert.ToSingle(n.Attributes["y"]?.Value);
         deco.loc.z = Convert.ToSingle(n.Attributes["z"]?.Value);
         deco.flip = n.Attributes["flip"]?.Value == "1";
         deco.scale = Convert.ToSingle(n.Attributes["scale"]?.Value);

         decorators.Add(deco);
     }
 }            
          

这一步遍历获取到的节点,并读取节点中属性的值。你可以看到在Attributes["x"]?后跟了个"?",表示这是个可空类型,如果它为null,就不会自动使用其Value属性;如果不为null,才使用。这个问号可以避免报错。

在这一步,我将读取到的值一个一个赋予我创建的类对象。


总结

读取XML文件的步骤:

  1. 获取XML文件
  2. 读取XML文件的节点
  3. 操作XML文件节点的值