[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Any reasonably complete JSON-RPC ORB projects?

ruby talk

1/26/2005 9:36:00 PM

Mixed in with the current churn about JavaScript and XmlHttpRequest
stuff is JSON [0] and JSON-RPC [1]. I've done some assorted RPC stuff
with XmlHttpRequest, but the JSON part looks interesting because it
transfers fewer bytes and may make it easier to build "standardized"
browser services in Ruby. (JSON is JavaScript object notation; like
YAML with C curly-braces instead of Python indentation, and JSON-RPC
is like XML-RPC, but with JSON.)

There is a Java project [2] that seems to do a good job of making the
RPC calls fairly transparent. The client code fetches a list of
methods from the server, and can then invoke method calls almost as if
it had local references to server objects. On the server, one can
register objects with some RPC proxy bridge thing so you don't have to
explicitly enumerate what method calls are available to the client.

So, I have some code that steals some ideas from the Java project
(and, in the browser, uses the same jsonrpc.js file), and works quite
well. Before I get too crazy with this, though, I was wondering if
someone had hadn't already done this (and gotten further than I have).

I've seen two JSON-related Ruby projects; one is a dead-simple JSON
parser [3] (I copped that, too), and the other appears to be an
attempt at Ruby-object JSON serialization, but incomplete[4].

Thanks,

James Britt




[0] http://www.crockford.com/JSON/...
[1] http://jso...
[2] http://oss.metaparadigm.co...
[3] http://rubyforge.org/snippet/detail.php?type=snippet...
[4]


3 Answers

Dido Sevilla

1/26/2005 10:59:00 PM

0

I had nearly forgotten this, but a year or so ago I actually wrote
JSON parsers for Ruby and OCaml. They're all here:

http://sourceforge.net/pro...


James Britt

1/27/2005 1:01:00 AM

0

Dido Sevilla wrote:
> I had nearly forgotten this, but a year or so ago I actually wrote
> JSON parsers for Ruby and OCaml. They're all here:
>
> http://sourceforge.net/pro...

Yes, I think I was looking at this earlier. I'm not clear, though, on
what it does.

I saw some JSON parsing code, and (I think) some object-to-JSON and
array-to-JSON code.

But I'm not sure how I would use it.


James


Florian Frank

1/27/2005 9:58:00 PM

0

ruby talk wrote:

>Mixed in with the current churn about JavaScript and XmlHttpRequest
>stuff is JSON [0] and JSON-RPC [1]. I've done some assorted RPC stuff
>with XmlHttpRequest, but the JSON part looks interesting because it
>transfers fewer bytes and may make it easier to build "standardized"
>browser services in Ruby. (JSON is JavaScript object notation; like
>YAML with C curly-braces instead of Python indentation, and JSON-RPC
>is like XML-RPC, but with JSON.)
>
>
I've recently written a JSON parser based on Ruby's StringScanner class,
that doesn't support the unicode stuff of the specifcation fully yet. A
few small examples how it can be used come here:

> puts
JSON.pretty_unparse([1,{"foo"=>2,"bar"=>[true,false,nil]},"baz",3.14])
[
1,
{
"foo":2,
"bar":[
true,
false,
null
]
},
"baz",
3.14
]

> p JSON.parse([1,{"foo"=>2,"bar"=>[true,false,nil]},"baz",3.14].to_json)
#=> [1, {"foo"=>2, "bar"=>[true, false, nil]}, "baz", 3.14]

I have attached a copy of the core class to this mail, but I wonder, if
I should do a real release (with tests and stuff) on Rubyforge, if you
or other people want to build something bigger with it.

--
Florian Frank

require 'strscan'

module JSON
JSONError = Class.new StandardError
ParserError = Class.new JSONError
CircularDatastructure = Class.new JSONError

class Parser < StringScanner

STRING = /"((?:\\"|[^"])*)"/
INTEGER = /\d+/
FLOAT = /-?\d+\.(\d*)(?:e[+-]?\d+)?/i
OBJECT_OPEN = /\{/
OBJECT_CLOSE = /\}/
ARRAY_OPEN = /\[/
ARRAY_CLOSE = /\]/
PAIR_DELIMITER = /:/
COLLECTION_DELIMITER = /,/
TRUE = /true/
FALSE = /false/
NULL = /null/
IGNORE = /\s+/

UNPARSED = Object.new

def parse
reset
until eos?
case
when scan(ARRAY_OPEN)
return parse_array
when scan(OBJECT_OPEN)
return parse_object
when scan(IGNORE)
;
when (value = parse_value) != UNPARSED
return value
else
raise ParserError, "source '#{peek(20)}' not in JSON!"
end
end
end

private

def parse_string
if scan(STRING)
return '' if self[1].empty?
self[1].gsub(/\\(?:[\\bfnrt"]|u([A-Fa-f\d]{4}))/) do
case $~[0]
when '\\\\' then '\\'
when '\\b' then "\b"
when '\\f' then "\f"
when '\\n' then "\n"
when '\\r' then "\r"
when '\\t' then "\t"
when '\"' then '"'
else $~[1].to_i(16).chr # TODO atm only unicode characters <= 0x127
end
end
else
UNPARSED
end
end

def parse_value
case
when scan(FLOAT)
Float(self[0])
when scan(INTEGER)
Integer(self[0])
when scan(TRUE)
true
when scan(FALSE)
false
when scan(NULL)
nil
when (string = parse_string) != UNPARSED
string
when scan(ARRAY_OPEN)
parse_array
when scan(OBJECT_OPEN)
parse_object
else
UNPARSED
end
end

def parse_array
result = []
until eos?
case
when (value = parse_value) != UNPARSED
result << value
scan(IGNORE)
unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
end
when scan(ARRAY_CLOSE)
break
when scan(IGNORE)
;
else
raise ParserError, "unexpected token in array at '#{peek(20)}'!"
end
end
result
end

def parse_object
result = {}
until eos?
case
when (string = parse_string) != UNPARSED
scan(IGNORE)
unless scan(PAIR_DELIMITER)
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
end
scan(IGNORE)
if (value = parse_value) != UNPARSED
result[string] = value
scan(IGNORE)
unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
raise ParserError,
"expected ',' or '}' in object at '#{peek(20)}'!"
end
else
raise ParserError, "expected value in object at '#{peek(20)}'!"
end
when scan(OBJECT_CLOSE)
break
when scan(IGNORE)
;
else
raise ParserError, "unexpected token in object at '#{peek(20)}'!"
end
end
result
end
end

class State
def self.from_state(opts)
case opts
when self
opts
when Hash
new(opts)
else
new
end
end

def initialize(opts = {})
@indent = opts[:indent] || ''
@object_nl = opts[:object_nl] || ''
@array_nl = opts[:array_nl] || ''
@seen = {}
end

attr_accessor :indent
attr_accessor :object_nl
attr_accessor :array_nl

def seen?(object)
@seen.key?(object.__id__)
end

def remember(object)
@seen[object.__id__] = true
end

def forget(object)
@seen.delete object.__id__
end
end

module_function

def parse(source)
Parser.new(source).parse
end

def unparse(obj, state = nil)
state = JSON::State.from_state(state)
obj.to_json(state)
end

def pretty_unparse(obj)
state = JSON::State.new(
:indent => ' ',
:object_nl => "\n",
:array_nl => "\n"
)
obj.to_json(state)
end
end

class Object
def to_json(*args)
to_s.to_json
end
end

class Hash
def to_json(state = nil, depth = 0)
state = JSON::State.from_state(state)
json_check_circular(state) { json_transform(state, depth) }
end

private

def json_check_circular(state)
if state
state.seen?(self) and raise JSON::CircularDatastructure,
"circular datastructures not supported!"
state.remember self
end
yield
ensure
state and state.forget self
end

def json_shift(state, depth)
state and not state.object_nl.empty? or return ''
state.indent * depth
end

def json_transform(state, depth)
delim = ','
delim << state.object_nl if state
result = ''
result << '{'
result << state.object_nl if state
result << map { |string,value|
json_shift(state, depth + 1) <<
string.to_json(state, depth + 1) <<
':' << value.to_json(state, depth + 1)
}.join(delim)
result << state.object_nl if state
result << json_shift(state, depth)
result << '}'
result
end
end

class Array
def to_json(state = nil, depth = 0)
state = JSON::State.from_state(state)
json_check_circular(state) { json_transform(state, depth) }
end

private

def json_check_circular(state)
if state
state.seen?(self) and raise JSON::CircularDatastructure,
"circular datastructures not supported!"
state.remember self
end
yield
ensure
state and state.forget self
end

def json_shift(state, depth)
state and not state.array_nl.empty? or return ''
state.indent * depth
end

def json_transform(state, depth)
delim = ','
delim << state.array_nl if state
result = ''
result = '['
result << state.array_nl if state
result << map { |value|
json_shift(state, depth + 1) << value.to_json(state, depth + 1)
}.join(delim)
result << state.array_nl if state
result << json_shift(state, depth)
result << ']'
result
end
end

class Fixnum
def to_json(*args) to_s end
end

class Float
def to_json(*args) to_s end
end

class String
def to_json(*args)
'"' << gsub(/["\\\0-\037]/) do
case $~[0]
when '"' then '\\"'
when '\\' then '\\\\'
when "\b" then '\b'
when "\f" then '\f'
when "\n" then '\n'
when "\r" then '\r'
when "\t" then '\t'
else "\\u\%04x" % $~[0][0]
end
end << '"'
end
end

class TrueClass
def to_json(*args) to_s end
end

class FalseClass
def to_json(*args) to_s end
end

class NilClass
def to_json(*args) 'null' end
end

module Kernel
def j(*objs)
objs.each do |obj|
puts JSON::unparse(obj)
end
nil
end

def jj(*objs)
objs.each do |obj|
puts JSON::pretty_unparse(obj)
end
nil
end
end
# vim: set et sw=2 ts=2: