The RPG Maker Resource Kit

RMRK RPG Maker Creation => VX Ace => VXA Scripts Database => Topic started by: game_guy on February 27, 2012, 06:29:44 AM

Title: [VXA] JSON Encoder/Decoder
Post by: game_guy on February 27, 2012, 06:29:44 AM
JSON Encoder/Decoder
Authors: game_guy
Version: 1.1
Type: Script Utility

Introduction

Quote
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate.
This is a simple JSON Parser or Decoder. It'll take JSON thats been formatted into a string and decode it into the proper object. You can also encode certain Ruby objects into JSON.

This is a scripter's utility and isn't meant to add any new functionality or prove usefulness to non-scripters.

Features


Screenshots

N/A

Demo

No demo.

Script

Spoiler for:
Code: [Select]
#===============================================================================
# JSON Encoder/Decoder
# Version 1.1
# Author: game_guy
#-------------------------------------------------------------------------------
# Intro:
# JSON (JavaScript Object Notation) is a lightweight data-interchange
# format. It is easy for humans to read and write. It is easy for machines to
# parse and generate.
# This is a simple JSON Parser or Decoder. It'll take JSON thats been
# formatted into a string and decode it into the proper object.
# This script can also encode certain ruby objects into JSON.
#
# Features:
# Decodes JSON format into ruby strings, arrays, hashes, integers, booleans.
#
# Instructions:
# This is a scripters utility. To decode JSON data, call
# JSON.decode("json string")
# -Depending on "json string", this method can return any of the values:
#  -Integer
#  -String
#  -Boolean
#  -Hash
#  -Array
#  -Nil
#
# To Encode objects, use
# JSON.encode(object)
# -This will return a string with JSON. Object can be any one of the following
#  -Integer
#  -String
#  -Boolean
#  -Hash
#  -Array
#  -Nil
#
# Credits:
# game_guy ~ Creating it.
#===============================================================================
module JSON
 
  TOKEN_NONE = 0;
  TOKEN_CURLY_OPEN = 1;
  TOKEN_CURLY_CLOSED = 2;
  TOKEN_SQUARED_OPEN = 3;
  TOKEN_SQUARED_CLOSED = 4;
  TOKEN_COLON = 5;
  TOKEN_COMMA = 6;
  TOKEN_STRING = 7;
  TOKEN_NUMBER = 8;
  TOKEN_TRUE = 9;
  TOKEN_FALSE = 10;
  TOKEN_NULL = 11;
 
  @index = 0
  @json = ""
  @length = 0
 
  def self.decode(json)
    @json = json
    @index = 0
    @length = @json.length
    return self.parse
  end
 
  def self.encode(obj)
    if obj.is_a?(Hash)
      return self.encode_hash(obj)
    elsif obj.is_a?(Array)
      return self.encode_array(obj)
    elsif obj.is_a?(Fixnum) || obj.is_a?(Float)
      return self.encode_integer(obj)
    elsif obj.is_a?(String)
      return self.encode_string(obj)
    elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
      return self.encode_bool(obj)
    elsif obj.is_a?(NilClass)
      return "null"
    end
    return nil
  end
 
  def self.encode_hash(hash)
    string = "{"
    hash.each_key {|key|
      string += "\"#{key}\":" + self.encode(hash[key]).to_s + ","
    }
    string[string.size - 1, 1] = "}"
    return string
  end
 
  def self.encode_array(array)
    string = "["
    array.each {|i|
      string += self.encode(i).to_s + ","
    }
    string[string.size - 1, 1] = "]"
    return string
  end
 
  def self.encode_string(string)
    return "\"#{string}\""
  end
 
  def self.encode_integer(int)
    return int.to_s
  end
 
  def self.encode_bool(bool)
    return (bool.is_a?(TrueClass) ? "true" : "false")
  end
 
  def self.next_token(debug = 0)
    char = @json[@index, 1]
    @index += 1
    case char
    when '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'
      return TOKEN_NUMBER
    when '{'
      return TOKEN_CURLY_OPEN
    when '}'
      return TOKEN_CURLY_CLOSED
    when '"'
      return TOKEN_STRING
    when ','
      return TOKEN_COMMA
    when '['
      return TOKEN_SQUARED_OPEN
    when ']'
      return TOKEN_SQUARED_CLOSED
    when ':'
      return TOKEN_COLON
    end
    @index -= 1
    if @json[@index, 5] == "false"
      @index += 5
      return TOKEN_FALSE
    elsif @json[@index, 4] == "true"
      @index += 4
      return TOKEN_TRUE
    elsif @json[@index, 4] == "null"
      @index += 4
      return TOKEN_NULL
    end
    return TOKEN_NONE
  end
 
  def self.parse(debug = 0)
    complete = false
    while !complete
      if @index >= @length
        break
      end
      token = self.next_token
      case token
      when TOKEN_NONE
        return nil
      when TOKEN_NUMBER
        return self.parse_number
      when TOKEN_CURLY_OPEN
        return self.parse_object
      when TOKEN_STRING
        return self.parse_string
      when TOKEN_SQUARED_OPEN
        return self.parse_array
      when TOKEN_TRUE
        return true
      when TOKEN_FALSE
        return false
      when TOKEN_NULL
        return nil
      end
    end
  end
 
  def self.parse_object
    obj = {}
    complete = false
    while !complete
      token = self.next_token
      if token == TOKEN_CURLY_CLOSED
        complete = true
        break
      elsif token == TOKEN_NONE
        return nil
      elsif token == TOKEN_COMMA
      else
        name = self.parse_string
        return nil if name == nil
        token = self.next_token
        return nil if token != TOKEN_COLON
        value = self.parse
        obj[name] = value
      end
    end
    return obj
  end
 
  def self.parse_string
    complete = false
    string = ""
    while !complete
      break if @index >= @length
      char = @json[@index, 1]
      @index += 1
      case char
      when '"'
        complete = true
        break
      else
        string += char.to_s
      end
    end
    if !complete
      return nil
    end
    return string
  end
 
  def self.parse_number
    @index -= 1
    negative = @json[@index, 1] == "-" ? true : false
    string = ""
    complete = false
    while !complete
      break if @index >= @length
      char = @json[@index, 1]
      @index += 1
      case char
      when "{", "}", ":", ",", "[", "]"
        @index -= 1
        complete = true
        break
      when "0", "1", "2", '3', '4', '5', '6', '7', '8', '9'
        string += char.to_s
      end
    end
    return string.to_i
  end
 
  def self.parse_array
    obj = []
    complete = false
    while !complete
      token = self.next_token(1)
      if token == TOKEN_SQUARED_CLOSED
        complete = true
        break
      elsif token == TOKEN_NONE
        return nil
      elsif token == TOKEN_COMMA
      else
        @index -= 1
        value = self.parse
        obj.push(value)
      end
    end
    return obj
  end
 
end

Instructions

Its recommended to place this script at the top, then it can be used in any script below it. To call the parsing method, use
Spoiler for JSON Decode Example:
Code: [Select]
JSON.decode(json)
Returns any of the following depending on "json":
-Integer
-String
-Boolean
-Hash
-Array
-Nil

Example:
Code: [Select]
json = '{"result":true,"profile":{"id":23,"display_name":"game_guy","group":{"id":"1","name":"Administrator","color":"#FF0000"}},"array":[0,1,2,3]}'
data = JSON.decode(json)

Output
Code: [Select]
data = {'result' => true, 'profile' => {'id' => 23, 'display_name' => 'game_guy', 'group' => {'id' => 1, 'name' => 'Administrator', 'color' => '#FF0000'}}, 'array' => [0, 1, 2, 3]}
As you can see, it supports multi-level hashes and the method returned a hash object since the string started with a "{". The first character is what defines what object its going to return.

Spoiler for JSON Encode Example:
This can encode the following Ruby objects into JSON format.
Code: [Select]
Integer
Boolean
String
Hash
Array
Nil
Here's your input.
Code: [Select]
data = {'result' => true, 'profile' => {'id' => 23, 'display_name' => 'game_guy', 'group' => {'id' => 1, 'name' => 'Administrator', 'color' => '#FF0000'}}, 'array' => [0, 1, 2, 3]}
json = JSON.encode(data)

Output
Code: [Select]
'{"result":true,"profile":{"id":23,"display_name":"game_guy","group":{"id":"1","name":"Administrator","color":"#FF0000"}},"array":[0,1,2,3]}'

Compatibility

Should work with anything.

Credits and Thanks


Author's Notes

Enjoy! :) You guys are lucky that VX Ace uses Ruby 1.9, or else this script wouldn't have been released. With the example code I posted, it takes a few seconds for Ruby 1.8 to parse it all.
Title: Re: [VXA] JSON Parser
Post by: modern algebra on February 27, 2012, 10:46:06 PM
Seems like it could be pretty neat!

P.S. I don't know what this says about my character, but I've always wanted to write a script that started with "J", but have never been able to do so. It, "X", "Y", and "Z" have all proven elusive.
Title: Re: [VXA] JSON Encoder/Decoder
Post by: game_guy on February 28, 2012, 02:53:51 AM
Ha, thanks! You might wanna update the Script Index, I changed the name and updated to 1.1. I added the encoder implementation. Whats great about JSON is that its data easily readable by humans and there are several languages that have an encoder/decoder. Its essentially cross-language way to serialize data.

And maybe to toss an idea out, if you wanted to create a script that started with a Y, you could create a YAML Encoder/Decoder.