Jump to content

[Snippet] Item prices and other metadata from the Grand Exchange API


Disposition

Recommended Posts

With this you can lookup:

  • Price
  • Icon (32x32 normal and 96x96 large)
  • Description
  • Name
  • Others

I've also included some caching to minimize API lookups but you can disable this if you want.

 

Usage:

 

Create a new instance of the API:

GrandExchangeApi api = new GrandExchangeApi();

Look up an item:

GELookupResult result = api.lookup(itemId);

Get data about the item:

int price = result.price;
String iconUrl = result.smallIconUrl;
String description = result.description; //etc

Snippet:

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility class for access to the Grand Exchange API.
 */
public class GrandExchangeApi {

    /**
     * The URL of the API endpoint.
     */
    private static final String API_LOCATION = "http://services.runescape.com/m=itemdb_oldschool/api/catalogue/detail.json?item=%d";

    /**
     * The cache in which items that have already been looked up are stored.
     */
    private final Map<Integer, GELookupResult> cache;

    /**
     * Creates a new Grand Exchange API instance, with caching enabled.
     */
    public GrandExchangeApi() {
        this(true);
    }

    /**
     * Creates a new Grand Exchange API instance.
     * @param cache Whether to enable caching of results.
     */
    public GrandExchangeApi(boolean cache) {
        if(cache) {
            this.cache = new HashMap<>();
        } else {
            this.cache = null;
        }
    }

    /**
     * If caching is enabled, clears the cache so that new results are fetched on lookup.
     */
    public void flushCache() {
        if(cache != null) {
            cache.clear();
        }
    }

    /**
     * Looks up an item using the Grand Exchange API. This method blocks while waiting for the API result.
     * @param itemId the id to look up.
     * @return the result returned by the api. May be null if an error has occurred.
     */
    public GELookupResult lookup(int itemId) {
        if(cache != null) {
            GELookupResult result = cache.get(itemId);
            if(result != null) {
                return result;
            }
        }

        String json;
        try {
            URL url = new URL(String.format(API_LOCATION, itemId));
            Scanner scan = new Scanner(url.openStream()).useDelimiter("\\A");
            json = scan.next();
            scan.close();
        } catch(IOException e) {
            return null;
        }

        GELookupResult result = parse(itemId, json);

        if(cache != null) {
            cache.put(itemId, result);
        }

        return result;
    }

    /**
     * Parses a GELookupResult from the JSON returned by the API.
     * @param itemId The item ID.
     * @param json The JSON returned by the server.
     * @return The serialized result.
     */
    private static GELookupResult parse(int itemId, String json) {
        Pattern pattern = Pattern.compile("\"(?<key>[^\"]+)\":\"(?<value>[^\"]+)\"");
        Matcher m = pattern.matcher(json);
        Map<String, String> results = new HashMap<>();
        while(m.find()) {
            results.put(m.group("key"), m.group("value"));
        }

        int price = 0;
        Matcher priceMatcher = Pattern.compile("\"price\":(?<price>\\d+)").matcher(json);
        if (priceMatcher.find()) {
            price = Integer.parseInt(priceMatcher.group("price"));
        }

        return new GELookupResult(
                results.get("icon"),
                results.get("icon_large"),
                results.get("type"),
                results.get("typeIcon"),
                results.get("name"),
                itemId,
                price
        );
    }

    /**
     * A class representing a result from an API lookup.
     */
    public static final class GELookupResult {
        public final String smallIconUrl, largeIconUrl, type, typeIcon, name;
        public final int id, price;

        private GELookupResult(String smallIconUrl,
                              String largeIconUrl,
                              String type, String typeIcon,
                              String name,
                              int id,
                              int price) {

            this.smallIconUrl = smallIconUrl;
            this.largeIconUrl = largeIconUrl;
            this.type = type;
            this.typeIcon = typeIcon;
            this.name = name;
            this.id = id;
            this.price = price;
        }
    }
}
Edited by Disposition
  • Like 5
Link to comment
Share on other sites

Nice work.

 

Caching the results is good practice, but not unless those cached results get updated too. Otherwise the information (such as price) will be out-of-date for the more volatile items.

 

That link gives a small JSon summary of the item, so don't worry about overloading Jagex's servers with requests. Just as long as it's not thousands of requests in a few short seconds, you'll be fine. They're a big and successful company. I'd suggest updating items every 2 minutes. :)

 

The best way to update the cache would be for the cached items to be mutable. It'll take a bit of tweaking to work, but avoid adding/removing directly to the cache, because that's inefficient.

  • Like 1
Link to comment
Share on other sites

Thanks for the feedback!

I know the caching is pretty rudimentary, but as far as I know GE prices only update once a day so it's not really a significant problem. Hadn't thought of making the results mutable though, the only problem I can see with doing that is in situations when you want to store the result as it was when you looked it up (maybe to monitor price changes).

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...