【问题标题】:XNA: Best way to load and read a XML file?XNA:加载和读取 XML 文件的最佳方式?
【发布时间】:2010-03-13 19:09:08
【问题描述】:

我很难完成这个看似简单的任务。我想像加载艺术资产一样轻松加载 XML 文件:

        content  = new ContentManager(Services);
        content.RootDirectory = "Content";
        Texture2d background = content.Load<Texture2D>("images\\ice");

我不知道该怎么做。这个tutorial 似乎很有帮助,但我如何获得StorageDevice 实例?

我现在确实有一些工作,但感觉很hacky:

public IDictionary<string, string> Get(string typeName)
        {
            IDictionary<String, String> result = new Dictionary<String, String>();
            xmlReader.Read(); // get past the XML declaration

            string element = null;
            string text = null;

            while (xmlReader.Read())
            {

                switch (xmlReader.NodeType)
                {
                    case XmlNodeType.Element:
                        element = xmlReader.Name;
                        break;
                    case XmlNodeType.Text:
                        text = xmlReader.Value;
                        break;
                }

                if (text != null && element != null)
                {
                    result[element] = text;
                    text = null;
                    element = null;
                }

            }
            return result;
        }

我将此应用于以下 XML 文件:

<?xml version="1.0" encoding="utf-8" ?>
<zombies>
  <zombie>
    <health>100</health>
    <positionX>23</positionX>
    <positionY>12</positionY>
    <speed>2</speed>
  </zombie>
</zombies>

并且能够通过这个单元测试:

    internal virtual IPersistentState CreateIPersistentState(string fullpath)
    {
        IPersistentState target = new ReadWriteXML(File.Open(fullpath, FileMode.Open));
        return target;
    }

    /// <summary>
    ///A test for Get with one zombie.
    ///</summary>
    //[TestMethod()]
    public void SimpleGetTest()
    {
        string fullPath = "C:\\pathTo\\Data\\SavedZombies.xml";
        IPersistentState target = CreateIPersistentState(fullPath);
        string typeName = "zombie"; 

        IDictionary<string, string> expected = new Dictionary<string, string>();
        expected["health"] = "100";
        expected["positionX"] = "23";
        expected["positionY"] = "12";
        expected["speed"] = "2";

        IDictionary<string, string> actual = target.Get(typeName);

        foreach (KeyValuePair<string, string> entry in expected)
        {
            Assert.AreEqual(entry.Value, expected[entry.Key]);
        }
    }

当前方法的缺点:文件加载效果不佳,并且将键与值匹配似乎比必要的工作量更大。另外,我怀疑这种方法会因为 XML 中有多个条目而崩溃。

我无法想象这是最佳的实现方式。

更新:根据@Peter Lillevold 的建议,我对此进行了一些更改:

    public IDictionary<string, string> Get(string typeName)
    {
        IDictionary<String, String> result = new Dictionary<String, String>();

        IEnumerable<XElement> zombieValues = root.Element(@typeName).Elements();

        //result["health"] = zombie.Element("health").ToString();

        IDictionary<string, XElement> nameToElement = zombieValues.ToDictionary(element => element.Name.ToString());

        foreach (KeyValuePair<string, XElement> entry in nameToElement)
        {
            result[entry.Key] = entry.Value.FirstNode.ToString();
        }

        return result;
    }

    public ReadWriteXML(string uri)
    {
        root = XElement.Load(uri);
    }

    internal virtual IPersistentState CreateIPersistentState(string fullpath)
    {
        return new ReadWriteXML(fullpath);
    }

    /// <summary>
    ///A test for Get with one zombie.
    ///</summary>
    [TestMethod()]
    public void SimpleGetTest()
    {
        IPersistentState target = CreateIPersistentState("../../../path/Data/SavedZombies.xml");
        string typeName = "zombie"; 

        IDictionary<string, string> expected = new Dictionary<string, string>();
        expected["health"] = "100";
        expected["positionX"] = "23";
        expected["positionY"] = "12";
        expected["speed"] = "2";

        IDictionary<string, string> actual = target.Get(typeName);

        foreach (KeyValuePair<string, string> entry in expected)
        {
            Assert.AreEqual(entry.Value, actual[entry.Key]);
        }
    }

加载仍然很糟糕,不知何故,我无法让单行 ToDictionary 与这两个 lambda 一起工作。我不得不求助于那个 foreach 循环。我在那里做错了什么?

【问题讨论】:

  • 你有一个错字。我的示例中的 @ 只能用于字符串文字,不能用于字符串变量或参数。
  • 您确实意识到 XNA 内容管道已经支持 XML 文件,不是吗?因此可以从字面上使用与加载艺术文件相同的语法来加载 XML 文件。

标签: c# xml xna


【解决方案1】:

还有新的闪亮的XElement(运动Linq to XML)。此示例将加载一个 xml 文件,查找僵尸并将值转储到字典中:

var doc = XElement.Load("filename");
var zombieValues = doc.Element("zombie").Elements();
var zombieDictionary = 
    zombieValues.ToDictionary(
        element => element.Name.ToString(), 
        element => element.Value);

如果您希望明确选择每个值(并使用强制转换自动转换为正确的值类型),您可以这样做:

var zombie = doc.Element("zombie");
var health = (int)zombie.Element("health");
var positionX = (int)zombie.Element("positionX");
var positionY = (int)zombie.Element("positionY");
var speed = (int)zombie.Element("speed");

更新:修正一些错别字并稍微清理一下,您的Get 方法应该如下所示:

public IDictionary<string, string> Get(string typeName)
{
    var zombie = root.Element(typeName);
    return zombie.Elements()
          .ToDictionary(
                  element => element.Name.ToString(),
                  element => element.Value);
}

【讨论】:

  • 还有,“zombie”前面的@符号是什么意思?
  • 另外,我怎样才能比 uri 更优雅地加载文件? (类似于内容管道?)
  • 抱歉,这里不需要@。当字符串应该是逐字的时,它被用作字符串的前缀。您可以考虑为您的文件类型制作自定义管道处理器。看看这个:msdn.microsoft.com/en-us/library/bb447754.aspx
  • 我认为其中一个.Elements() 电话是过度的。但除此之外,这让我的代码更加简洁。
【解决方案2】:
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(xmlString);

string health = doc["zombies"]["zombie"]["health"].InnerText;
// etc..

// or looping

foreach( XmlNode node in doc["zombies"].ChildNodes )
{
    string health = node["health"].InnerText;
    // etc...
}

或者这在 XNA 中不起作用?

【讨论】:

  • 我会试试的。但是首先从文件中获取 xmlString 的更好方法是什么?
  • 更新了代码,因为我犯了一个错误。 (doc.Load => doc.LoadXml)
  • XmlDocument 似乎没有包含在 .NET 的 XNA 发行版中,或者至少在我尝试使用它时它会抱怨。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2011-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多