将 LINQ to XML 用于简单的 XML 文件:矫枉过正?或者没有更糟
本文关键字:XML 或者 矫枉过正 文件 to LINQ 用于 简单 | 更新日期: 2023-09-27 18:31:07
我正在构建一个简单的自上而下的基于瓷砖的2D游戏,我正在尝试解析平铺地图编辑器(.tmx文件)的输出。对于那些不熟悉的人来说,TMX文件是XML文件,它使用图像中重复使用的瓷砖层来描述游戏地图。除了解析简单文本之外,我以前从未使用过任何东西,我想知道对于相当简单的 XML 文件,使用 LINQ 是否是执行此操作的最合适方法。
这是一个删节的 .tmx 文件:
<?xml version="1.0" encoding="UTF-8"?>
<map width="100" height="100" tilewidth="16" tileheight="16">
<tileset>
<!-- This stuff in here is mostly metadata that the map editor uses -->
</tileset>
<layer name="Background" width="100" height="100">
<data>
<tile gid="1" />
<tile gid="2" />
<tile gid="3" />
<tile gid="1" />
<tile gid="1" />
<tile gid="1" />
<!-- etc... -->
</data>
</layer>
<layer name="Foreground" width="100" height="100">
<data>
<!-- gid="0" means don't load a tile there. It should be an empty cell for that layer (and the layers beneath this are visible) -->
<tile gid="0" />
<tile gid="4" />
<!-- etc. -->
</data>
</layer>
<!-- More layers.... -->
</map>
如您所见,它相当简单(请注意,每个图层中的每个图块(100x100)都有一个"tile"元素)。现在在我看来,LINQ 的目的是从一个可能非常大且几乎类似于数据库的 xml 文件中获取非常具体的数据,而您实际上并不需要整个文件。大多数情况下,我将在这里做的是将每个"tile"元素的 gid 插入到一个表示我的应用程序中地图的数组中。
这是我处理层的代码:
public void AddLayer(XElement layerElement) {
TileMapLayer layer = new TileMapLayer(Convert.ToInt32(layerElement.Attribute("width")), Convert.ToInt32(layerElement.Attribute("height")));
layer.Name = (string)layerElement.Attribute("name");
layer.Opacity = Convert.ToDouble(layerElement.Attribute("opacity"));
layer.Visible = Convert.ToInt32(layerElement.Attribute("visible")) == 1;
if (layerElement.HasElements)
{
XElement data = layerElement.Element("data");
foreach (XElement tile in data.Elements())
{
layer.NextTile(Convert.ToInt32(tile.Attribute("gid")));
}
}
this.layers.Add(layer);
}
为了使我的问题更加简洁:当我浏览并关心每条数据(即,我按顺序循环访问和获取每个节点的所有子元素的数据)时,使用 LINQ to XML 会给我带来任何好处吗?比如 LINQ to XML 库的性能是否更好?,我对 LINQ 的不熟悉是否阻止我看到一种有效的方法来执行我想要的操作?等等?或者我真的应该使用不同的XML实用程序吗?
对于简单的检索和更新数据,LINQ to XML 似乎是 XML 解析的方钉/圆孔解决方案。XElement当然可以递归迭代,但我发现XPath查询更加简洁和易于阅读。
我发现 LINQ to XML 非常有用的地方是文档命名空间的操作。.NET 提供的其他框架不会以优雅的方式处理此问题。根据 http://msdn.microsoft.com/en-us/library/ecf3e2k0.aspx:
更改节点的前缀不会更改其命名空间。命名空间只能在创建节点时设置。保留树时,可能会保留新的命名空间属性以满足您设置的前缀。如果无法创建新命名空间,则会更改前缀,以便节点保留其本地名称和命名空间。
当然,这是一个深奥的要求,但我发现 LINQ to XML 在为 XPath 查询准备命名空间管理器方面也很有用。
public XmlNamespaceManager NamespaceManager { get; set; }
public XPathNavigator Navigator { get; set; }
public SuperDuperXMLQueryingClass(System.IO.Stream stream)
{
var namespaces = RetrieveNameSpaceMapFromXml(XDocument.Load(stream).Root);
Navigator = new XPathDocument(stream).CreateNavigator();
NamespaceManager = new XmlNamespaceManager(Navigator.NameTable);
foreach (var t in namespaces)
{
NamespaceManager.AddNamespace(t.Key, t.Value.NamespaceName);
}
}
// LINQ to XML mostly used here.
private static Dictionary<string, XNamespace> RetrieveNamespaceMapFromXDocumentRoot(XElement root)
{
if (root == null)
{ throw new ArgumentNullException("root"); }
return root.Attributes().Where(a => a.IsNamespaceDeclaration)
.GroupBy(a => (
a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName),
a => XNamespace.Get(a.Value)
)
.Where(g => g.Key != string.Empty)
.ToDictionary(g => g.Key, g => g.First());
}
public string DeliverFirstValueFromXPathQuery(string qry)
{
try
{
var iter = QueryXPathNavigatorUsingShortNamespaces(qry).GetEnumerator();
iter.MoveNext();
return iter.Current == null ? string.Empty : iter.Current.ToString();
}
catch (InvalidOperationException ex)
{
return "";
}
}
这使您可以选择使用 XPath 查询 XML 文档,而无需使用完整的 URI。
//For Example
DeliverFirstValueFromXPathQuery("/ns0:MyRoot/ns1:MySub/nsn:ValueHolder");
这里的总结是,每个框架都有一些重叠的功能集;但是,有些框架比其他框架更适合用于专业作业。