Reputation: 643
I'm working on building a simple top-down tile-based 2D game and I'm trying to parse the output of Tiled Map Editor (.tmx file). For those unfamiliar, TMX files are XML files which describe a game map using layers of re-used tiles from an image. I've never had to work with anything but parsing simple text before and I'm wondering if, for the case of a rather simple XML file, using LINQ is the most appropriate way to do this.
Here is an abridged .tmx file:
<?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>
As you can see, it's rather simple (note that there is a 'tile' element for each tile (100x100) in each layer). Now it seems to me that the purpose of LINQ is to get very specific data from what could perhaps be a very large and almost database-like xml file where you don't really need the whole file. Mostly what I'll be doing here is going through and inserting the gid for each of the 'tile' elements into an array which represents the map in my application.
Here is my code for processing a layer:
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);
}
To make my question more succinct: When I'm going through and I care about every piece of data (i.e. I'm iterating through and getting data for all the child elements of each node sequentially), does using LINQ to XML afford me any benefits? Like are the LINQ to XML libraries better performing?, does my unfamiliarity with LINQ stop me from seeing an efficient way of doing what I want?, etc.? Or should I really be using different XML utilities?
Upvotes: 2
Views: 286
Reputation: 83
For simply retrieving and updating data, LINQ to XML seems a square peg/round hole solution to XML parsing. XElement can of course be iterated over recursively, but I find XPath queries much more concise and easy to read.
Where I find LINQ to XML very useful is in manipulation of document namespaces. The other frameworks provided by .NET do not approach this in a graceful manner. According to http://msdn.microsoft.com/en-us/library/ecf3e2k0.aspx:
Changing the prefix of a node does not change its namespace. The namespace can only be set when the node is created. When you persist the tree, new namespace attributes may be persisted out to satisfy the prefix you set. If the new namespace cannot be created, then the prefix is changed so the node preserves its local name and namespace.
Granted, this is an esoteric requirement, but I find LINQ to XML useful in preparing the namespace manager for XPath querying as well.
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 "";
}
}
This gives you the option to use XPath to query your XML document without having to use the full URI.
//For Example
DeliverFirstValueFromXPathQuery("/ns0:MyRoot/ns1:MySub/nsn:ValueHolder");
The summary here is that each framework has some set of overlapping functionality; however, some are more graceful to use for specialized jobs than others.
Upvotes: 3