Any reasonably complete JSON-RPC ORB projects?

R

ruby talk

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/index.html
[1] http://json-rpc.org/
[2] http://oss.metaparadigm.com/jsonrpc/
[3] http://rubyforge.org/snippet/detail.php?type=snippet&id=29
[4]
 
J

James Britt

Dido said:
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/projects/json

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
 
F

Florian Frank

--------------000307050102030202010203
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

ruby said:
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


--------------000307050102030202010203
Content-Type: text/plain;
name="json.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="json.rb"

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[:eek:bject_nl] || ''
@array_nl = opts[:array_nl] || ''
@seen = {}
end

attr_accessor :indent
attr_accessor :eek:bject_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 => ' ',
:eek:bject_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:

--------------000307050102030202010203--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top