Wednesday 27 March 2013

Using C# to Get and Manipulate JSON

In my previous post I showed you how to see the JSON data that your browser was requesting from the Edgeworld server.  In this post I am going to show you how to mimic that request using C# and Visual Studio 2012.

When you look at the URL of the page request it has 2 basic parts
  • The root URL (http://sector15.c1.galactic.wonderhill.com/api/alliances/428295)
  • The URL parameters (_session_id, meltdown, reactor, user_id)
I do not know what the Edgeworld servers do with all of these parameters but I have discovered that the URL parameter of meltdown has a key value.  This value changes every time you log in to Edgeworld and it will time out once you log off.  This means that before you run a program to mimic the page request you need to log in to Edgeworld and use your developer tools to get this value and use it in your code.  The long number at the end of the string is the alliance ID.  So when you get this working for your alliance use your developer tools to find other alliance ID and track your enemies progress.

Step 1: Mimic the page request

The following code will mimic the page request.  I have created a few variables to make it easier to change out the meltdown value as mentioned above.

using System.IO;
using System.Net;

string fileContents;
string meltdown = "7463c45526b77360ffcb8d5e7f109c9d6a68048f";
string pagerequest = "http://sector15.c1.galactic.wonderhill.com/api/alliances/428295?%5Fsession%5Fid=48f319f393d1bca6ef3bed661658734a&meltdown=" + meltdown + "&reactor=4d545bca4d1d48b4d8f05685cc684a000849fb1c&user%5Fid=3608004";

WebRequest request = WebRequest.Create(@pagerequest);
WebResponse response = request.GetResponse();
Stream data = response.GetResponseStream();
string html = String.Empty;
using (StreamReader sr = new StreamReader(data))
{
    fileContents = sr.ReadToEnd();
}

For C# to use WebRequest you need to add using System.Net to the top of your program.  In the same way if you want to use Stream you need to add using System.IO.  This code will mimic the page request and save the contents of the request into a variable called fileContents.

Step 2: Parse the JSON data

Now that we have the JSON data saved into a variable it is time to parse through the data and convert it to a data structure that we can easily reference.

There are many different libraries and ways of doing this but the one I found the easiest to implement was JavaScriptSerializer.

using System.Web.Script.Serialization;

JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
PlayerData alliance = jsonSerializer.Deserialize<PlayerData>(fileContents);

To be able to use the JavaScriptSerializer you need to add a reference to your project.  To do this you need to go to Project > Add Reference. The reference that we are looking for is System.Web.Extensions and it can be found int Assemblies>Framework. Once the reference is added we need to add using System.Web.Script.Serialization to the top of our program.

The JSON data that comes from the web server is nested data that looks something like this:

{"response":
 {"timestamp":1364414863,"alliance":
  {"id":428295,"name":"ThisGameTooExpensive","description":"","members":
   [{"id":1828109,"name":"Door2Death","level":269}]
  }
 }
}

All the data is nested under response and, in this example, it has 2 variables (timestamp,alliance).  The alliance information is nested under alliance.  Then a variable under alliance is members which then nests all of the members of this particular alliance.  In order to parse through this we have to create a class to describe each nested level.  Below are four classes that parse out this data.

public class PlayerData
{
    public DataItem response { get; set; }
}
public class DataItem
{
    public int timestamp { get; set; }
    public Alliance alliance { get; set; }
    public List<Members> members { get; set; }
}
public class Alliance
{
    public int id { get; set; }
    public string name { get; set; }
}
public class Members
{
    public int id { get; set; }
    public string name { get; set; }
}

In the actual data that you will download through this method there will be more variables under each nested group.  You can simply add the variable names and the data will be added to your final data structure.

Step 3: Working with the new data structure

Now that the data is parsed into variables how do we work with it?  The advantage of Visual Studio in this case is that it will prompt you as you type out the variable structure starting with alliance once you have the above code in your program.

So if we wanted to print out the name of the alliance and then list all of the members and their respective levels to the console window we would use the code below:

Console.WriteLine(alliance.response.alliance.name);
Console.WriteLine();

for (int i = 0; i < alliance.response.members.Count;  i++)
{
    Console.WriteLine(alliance.response.members[i].id + " " + alliance.response.members[i].name);
}
Console.Read();

You can refer to a simple value like the alliance name because there is only one name or you can loop through and array of values like the list of members.  Once you understand these ideas you can then explore other things to do with the values.  Just displaying them in the console window isn't that helpful as you can see them in the alliance page.  However, if you were to save these values into an excel file and then save the values again in a week you can start tracking the progress of your alliance over time.

Thanks for reading. I have also demonstrated these ideas in the video below and the entire program is written out below the video.





using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;

namespace JSON_tutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            string fileContents;
            string meltdown = "7463c45526b77360ffcb8d5e7f109c9d6a68048f";
            string pagerequest = "http://sector15.c1.galactic.wonderhill.com/api/alliances/428295?%5Fsession%5Fid=48f319f393d1bca6ef3bed661658734a&meltdown=" + meltdown + "&reactor=4d545bca4d1d48b4d8f05685cc684a000849fb1c&user%5Fid=3608004";

            WebRequest request = WebRequest.Create(@pagerequest);
            WebResponse response = request.GetResponse();
            Stream data = response.GetResponseStream();
            string html = String.Empty;
            using (StreamReader sr = new StreamReader(data))
            {
                fileContents = sr.ReadToEnd();
            }
            JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
            PlayerData alliance = jsonSerializer.Deserialize<PlayerData>(fileContents);

            Console.WriteLine(alliance.response.alliance.name);
            Console.WriteLine();
            for (int i = 0; i < alliance.response.members.Count;  i++)
            {
                Console.WriteLine(alliance.response.members[i].id + " " + alliance.response.members[i].name);
            }
            Console.Read();
        }
    }
    public class PlayerData
    {
        public DataItem response { get; set; }
    }
    public class DataItem
    {
        public int timestamp { get; set; }
        public Alliance alliance { get; set; }
        public List<Members> members { get; set; }
    }
    public class Alliance
    {
        public int id { get; set; }
        public string name { get; set; }
    }
    public class Members
    {
        public int id { get; set; }
        public string name { get; set; }
    }
}


6 comments:

  1. Nice post. I have thought about building a better interface for the engineering lab. The API seems straight forward with a call using the part ID, but for the life of me I can't seem to find a good way to get a list of all the parts I have. When that window is created, it is all done with javascript... any idea on how to tease that info out?

    ReplyDelete
    Replies
    1. I haven't looked into the Eng Lab yet. I will look into that tonight and see if I can figure anything out there.

      Delete
    2. If you already understand the stuff in my previous posts please try to use this:

      api/player/equipment?meltdown=blah&_session_id=null&reactor=blah&user_id=blah

      Cheers

      Delete
  2. Very nice!

    public class Equipment
    {
    public int id { get; set; }
    public string type { get; set; }
    public int level { get; set; }
    public int durability { get; set; }
    public bool equipped { get; set; }
    public int? base_id { get; set; }
    public string building_type { get; set; }
    }

    public class Response
    {
    public int timestamp { get; set; }
    public List equipment { get; set; }
    public bool success { get; set; }
    }

    public class RootObject
    {
    public Response response { get; set; }
    public string x { get; set; }
    public string y { get; set; }
    }

    ReplyDelete
    Replies
    1. Well you definitely understood the previous post :). Nice work. I will have to find a use for that now.

      I am now working on a map monitoring tool that can help keep track of base counts so I know when we are attacked. I added a new tab to this page to list out the known APIs. Thanks for your help.

      Delete
  3. thanks, how can i handle the case that i don't have the namse of the propoties in the json file ?

    ReplyDelete