0

This is the snippet of the XML file that I access via a [URL][1] that I need to focus on.

<imgdir name="portal">

<imgdir name="0">
<string name="pn" value="sp"/>
<int name="pt" value="0"/>
<int name="x" value="-288"/>
<int name="y" value="27"/>
<int name="tm" value="999999999"/>
<string name="tn" value=""/>
</imgdir>

<imgdir name="1">
<string name="pn" value="in00"/>
<int name="pt" value="7"/>
<int name="x" value="74"/>
<int name="y" value="154"/>
<int name="tm" value="999999999"/>
<string name="tn" value=""/>
<int name="horizontalImpact" value="0"/>
<string name="script" value="talkToMai"/>
<int name="hideTooltip" value="0"/>
<int name="onlyOnce" value="0"/>
<int name="delay" value="0"/>
</imgdir>

<imgdir name="2">
<string name="pn" value="sp"/>
<int name="pt" value="0"/>
<int name="x" value="-349"/>
<int name="y" value="-45"/>
<int name="tm" value="999999999"/>
<string name="tn" value=""/>
</imgdir>

<imgdir name="3">
<string name="pn" value="sp"/>
<int name="pt" value="0"/>
<int name="x" value="257"/>
<int name="y" value="132"/>
<int name="tm" value="999999999"/>
<string name="tn" value=""/>
</imgdir>

<imgdir name="4"> // PLOT THIS PORTAL
<string name="pn" value="east00"/>
<int name="pt" value="2"/>
<int name="x" value="683"/>
<int name="y" value="211"/>
<int name="tm" value="4000026"/>
<string name="tn" value="west00"/>
<int name="horizontalImpact" value="0"/>
<string name="script" value=""/>
<int name="hideTooltip" value="0"/>
<int name="onlyOnce" value="0"/>
<int name="delay" value="0"/>
</imgdir>

<imgdir name="5">
<string name="pn" value="sp"/>
<int name="pt" value="0"/>
<int name="x" value="213"/>
<int name="y" value="101"/>
<int name="tm" value="999999999"/>
<string name="tn" value=""/>
</imgdir>

<imgdir name="6"> // PLOT THIS PORTAL
<string name="pn" value="west00"/>
<int name="pt" value="2"/>
<int name="x" value="-426"/>
<int name="y" value="212"/>
<int name="tm" value="4000020"/>
<string name="tn" value="east00"/>
<string name="script" value=""/>
<int name="hideTooltip" value="0"/>
<int name="delay" value="0"/>
<int name="onlyOnce" value="0"/>
</imgdir>

</imgdir>
</imgdir>

I already read through the same XML but a different section in this fashion.

public static int getWidth(string id)
    {
        try
        {
            var uri = "http://[redacted]/INFO/" + id + ".img.xml";
            var doc = XDocument.Load(uri);

            return (int)doc.Descendants("int").First(x => (string)x.Attribute("name") == "width").Attribute("value");
        }
        catch
        {
            return 0;
        }
    }

If you look at the snippet of the XML file I posted above, you will notice that I put comments on the "portals" I need to plot onto my map (portals # 4 & 6). What I do not understand is how can I edit the above function to loop through the "portals" and check if the portal names 'pn' and 'tn' contain a value (does not matter what the value is) and to check if the additional name 'tm' does not contain the value 999999999, and if the aforementioned is all 'true', then to grab the x and y values and use them to draw the portal, and loop ofc for rest of the portals.

I already have the draw function and what not, I just do not understand how to loop through the XML code posted, check for certain names and values (pn & tn & tm), then to grab the x and y if it meets the conditions, and then finally use it in the draw function and repeat until nothing left.

I hope you understand this, thanks.

1 Answer 1

1

Since you have opted for linq to xml, this will be very easy. Basically, you can use Elements() to chip away at things until you get what you want, or pull out some dynamite and use Descendants(). The amount of mess is entirely up to you. I will be using the Elements() approach. However Descendants() calls are syntactically pretty much the same, but the amount of filtering in this case is roughly the same.

Here's how you can get the first level of imgdirs:

IEnumerable<XElement> data = doc.Elements("imgdir").Where(el => el.HasAttributes);

This will yield all of the top level elements that have an attribute {info, back, life, etc., but no children of them}. Now that we have something to iterate through, you can either iterate through the elements, or you can have all your methods accept a single XElement parameter. That way you can pass in only what you need.

For all of your various methods that get out the needed data to pass, just write another query to limit things a little more. In the case presented, you can use this:

XElement portal = data.Elements().SingleOrDefault(el => el.Attribute("name").Value == "portal");

Now you have a single XElement that can be split even further. Time to get the most granular imgdirs:

IEnumerable<XElement> img = portal.Elements("imgdir");

Time to iterate and sort out the big from the little:

foreach (var elements in img) // or you can put this in place of "img" : portal.Elements("imgdir")
{
    foreach (var el in elements.Elements())
    {
        if (el.Attribute("name").Value == "pn" && el.Attribute("value").Value != "sp")
        {
            // It's a big 'un!
        }
        else
        {
            // Smaller one.
        }
    }
}

NOTE: you can add in tn & tm with their respective values into that conditional statement. Also, don't overlook the nested iterations, you probably could put it into another query, but I haven't figured that one out yet.

As a tip, if you want to check if there is some data without throwing an exception, just put in something resembling this:

if (element != default(XElement))

One final note, you can write out most of this as one gigantic query, but that would be a nightmare to debug.

EDIT: Here's a sample rewrite to getWidth:

public static int GetInt(XElement data, string attribName)
{
    foreach (var element in data.Elements("int"))
    {
        if (element.Attribute("name").Value == attribName)
        {
            return int.Parse(element.Attribute("value").Value);
        }
    }

    throw new ArgumentException("Attribute name does not exist in provided data.");
}

It would be good practice to put the exception in there due to the probability that this method can be reused in the context of your program and that if another dev misuses it, it will point out the problem.

Sign up to request clarification or add additional context in comments.

8 Comments

I need to see whether or not within a portal, pn and tn have a value (anything other than blank/empty) and if tm does not equal 999999999 and if so, then to get the x and y value. Otherwise, I will try to work with what you gave me.
what do you mean it is a smaller one?
I was using the terms big and small to denote the difference in line counts. Basically 1, 4 & 6 all contain data in pn, tn and tm other than 0, 2, 3 & 5. I did test the code in there, so you can probably copy and paste it into the proper location.
Basically, when you get to the conditional statement, just replace the comments with calls to getPortalXY and getWidth respectively. As a design to consider, you can have those methods accept an XElement instead of all the ints and strings. I'll update the answer to include a rewrite to getWidth that should put you in a better standing when working with linq-to-xml.
I just came back and noticed you posted a general function to get a integer (assuming the value of a name)! Just a FYI, the getWidth is not related to the question at all; however, I thank thee for your sample rewrite! So instead of having 4 separate functions (all contain same code as getWidth except for the value *ex. getHeight, getCenterX, etc), I would just load the xml once and use your sample rewrite? Also, for your previous code (before your EDIT), I can put another if function inside the if for tn & tm right? Thank you, I appreciate this as I am not generally good with XML.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.