Thursday, August 29, 2013

ZKB Json parsing in Java

Json parsing in Java using gson:

   While looking into pulling kill data using zkillboard's API, I had some trouble learning how to parse Json.  This is partially because its the first time that I was using Json, and partially because I am still new to Java as a whole.  Below is an example of how to parse kill data from ZKB.

    Some notes on parsing data from ZKB:
  • You need ~15 second program pause after every call to ZKB.  This is not reflected in this code as it parses a single page of the call.
  • Please see the ZKB Api page for more information on how to make the calls you want.

package com.JEves.test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import com.btr.proxy.search.ProxySearch;
import com.btr.proxy.search.ProxySearch.Strategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class SimpleKbParser   implements JsonDeserializer {

 public String m_value;
 /**
  * @param args
  */
 @SuppressWarnings("deprecation")
 public static void main(String[] args) {
  ProxySearch proxySearch = new ProxySearch();
  proxySearch.addStrategy(Strategy.OS_DEFAULT); 
  proxySearch.addStrategy(Strategy.JAVA); 
  proxySearch.addStrategy(Strategy.BROWSER); 
  ProxySelector proxySelector = proxySearch.getProxySelector(); 

  if(proxySelector != null) {
   ProxySelector.setDefault(proxySelector); 
   URI home = URI.create("http://www.google.com"); 
   //System.out.println("ProxySelector: " + proxySelector); 
   //System.out.println("URI: " + home); 
   List proxyList = proxySelector.select(home); 
   if (proxyList != null && !proxyList.isEmpty()) { 
    for (Proxy proxy : proxyList) { 
     //System.out.println(proxy); 
     SocketAddress address = proxy.address(); 
     if (address instanceof InetSocketAddress) { 
      String host = ((InetSocketAddress) address).getHostName(); 
      String port = Integer.toString(((InetSocketAddress) address).getPort()); 
      System.setProperty("http.proxyHost", host); 
      System.setProperty("http.proxyPort", port); 
     } 
    } 
   }
  }
  URLConnection.setDefaultRequestProperty("Accept-Encoding", "gzip, deflate");
  URLConnection.setDefaultRequestProperty("User-Agent", "Valkrr Dragonsworn");
  BufferedReader reader;
  String itemUrl = "http://zkillboard.com/api/kills/regionID/10000002/";
  try {
   GsonBuilder gsonBuilder = new GsonBuilder();
      gsonBuilder.registerTypeAdapter(SimpleKbParser.class, new SimpleKbParser());
      Gson gson = gsonBuilder.create();
   reader = new BufferedReader(new InputStreamReader(new URL(itemUrl).openStream()));
   SimpleKbParser[] kills = gson.fromJson(reader, SimpleKbParser[].class);
   for(int i = 0; i < kills.length; i++) {
    System.out.println("Kill: " + i);
    System.out.println(kills[i].m_value + "\n");
   }
  }
  catch(Exception e) {
   e.printStackTrace();
  }
 }

 @Override
 public SimpleKbParser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
  SimpleKbParser ret = new SimpleKbParser();
  int killID = ((JsonObject) json).get("killID").getAsInt();
  int solarSystemID = ((JsonObject) json).get("solarSystemID").getAsInt();
  String killTime = ((JsonObject) json).get("killTime").getAsString();
  int moonID = ((JsonObject) json).get("moonID").getAsInt();
  ret.m_value += "killID: " + killID;
  ret.m_value += "\nsolarSystemID: " + solarSystemID;
  ret.m_value += "\nkillTime: " + killTime;
  ret.m_value += "\nmoonID: " + moonID;
  return ret;
 }

}

I will go through the above class section by section.  I am assuming you will be copying this into an editor so I will be referring to line numbers.

Lines 1-23:

   Your basic setup and import steps.

Line 25:

   public class SimpleKbParser   implements JsonDeserializer<SimpleKbParser> {

  JsonDeserializer is what makes the magic easy an quick.  This basically allows you to have your storage class get 1 element at a time from the series of elements and have to worry about parsing just that one element.  

Lines 33-57:

   I do part of my programming behind a firewall and the other part outside of a firewall.  This piece of code allows the script to auto detect the correct proxy type and use that by default. This requires proxy-vole to be linked to the project, so if you are not worried about firewalls, remove these lines along with lines 15 and 16.

Lines 58-75:

   An extremely basic program for grabbing and printing kill data.

   The key line here is line 67:
      SimpleKbParser[] kills = gson.fromJson(reader, SimpleKbParser[].class);
    This line tells gson that you want to parse the BufferedReader with your json in it into an array of SimpleKbParser objects.

Lines 79-90:

   Here is the function overloaded from JsonDeserializer.  For this example I just pulled some of the simple information out of the json and added it to a member string to be later printed.  Depending on what you are trying to do with this information, you will have to decide what information you want.

int killID = ((JsonObject) json).get("killID").getAsInt();

   You have to cast the JsonElement json to JsonObject, then run the get(Element Tag) which returns a JsonElement.  You run the getAs<Type>() function on the resulting JsonElement to get the value in a usable format.

JsonArray attackers = ((JsonObject) json).get("attackers").getAsJsonArray();
for(int i = 0; i < attackers.size(); i++) {
   JsonObject attacker = attackers.get(i).getAsJsonObject();
}

   For array data inside a json element, you cast as above, but you get it as a JsonArray type.  You can then iterate over that array, getting each object as a JsonObject to make getting values easier.


Summary:

   So, we learned how to get ZKB data into our programs in just a few dozen lines of actual code, less that 100 total lines.  There are some things not covered here to keep it simple, line how to effectively iterate over pages in a query and pretty much any error checking.  All of the coding is done in Eclipse build v21.1.0 using the ADT install package.  Other compilers might have compiler specific requirements that could prevent the above code from compiling.

   My source code can be found at Rapid Assembly.  This include git source repository and an occasional executable of my tool.  I have not made much use of the zkillboard parser yet, but I have plans to do so as part of a product selection GUI.

No comments:

Post a Comment