質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

2440閲覧

JSONっぽい値の、Rubyでのハンドリング方法

maisumakun

総合スコア145121

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/01/26 03:57

編集2018/01/26 03:59

JavaScriptのオブジェクトリテラルに対して、JSONでは「文字列やキーは二重引用符でくくる」ということが違ってきます。

javascript

1// JSONとして正しい 2{"foo": "bar"} 3 4// キーは裸にできないので不適切 5{foo: "bar"} 6 7// 一重引用符では区切れないので不適切 8{'foo': 'bar'}

ところが、ひょんな拍子に、JSONと比較して「文字列やキーの区切りだけ違う(こともある)」データをハンドリングしないといけなくなりました。となると、ふつうのJSONパーサーは役に立ちません。

とりあえず今回はソースが信頼できるものだったので、ExecJSに投げ込んでオブジェクトとして取得すれば、いちおう解釈できそうなのですが、やりたいことに対して大げさな気もしますし、信頼できないデータソースに対してこんなことはできません。

「自分でパーサーを書くなど、いちから実装する」「JavaScript処理系に投げる」の両極端に対して、うまく既存のJSONパーサーに少し手を加えるなどで乗り切れる方法はないものでしょうか。


環境

  • Ruby 2.4 on Amazon Linux (x64)
  • ExecJS + therubyracer
  • MultiJSON + Oj

(もちろん、状況によってはgemを追加することも可能です)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

パーサを書いてみました。JSON ではないので JSAN と名付けています。

ruby

1module JSAN 2 class Context 3 WHITE_SPACES = [" ", "\t", "\r", "\n"] 4 NUMBER_LETTERS = '0123456789+-.eE' 5 HEX_LETTERS = '0123456789abcdef' 6 def initialize(s) 7 @buf = s 8 @index = 0 9 @length = s.size 10 end 11 def skip_white 12 while WHITE_SPACES.include?(@buf[@index]) do 13 @index += 1 14 end 15 end 16 def has_next? 17 @index < @length 18 end 19 def next 20 b = @buf[@index] 21 @index += 1 22 b 23 end 24 def back 25 @index -= 1 26 end 27 def current 28 return @buf[@index] 29 end 30 def error(msg) 31 raise "#{msg}: #{@buf[@index...-1]}" 32 end 33 def parse_constant(expect, value) 34 s = '' 35 pos = @index 36 while self.has_next? 37 c = self.next 38 unless expect.include?(c) 39 if s == expect 40 self.back 41 return value 42 end 43 @index = pos 44 error 'Unknown token' 45 end 46 s += c 47 end 48 error 'Unknown token' 49 end 50 def parse_number 51 s = self.next 52 while self.has_next? 53 c = self.next 54 unless NUMBER_LETTERS.include?(c) 55 self.back 56 break 57 end 58 s += c 59 end 60 if s.include?('.') 61 return s.to_f 62 end 63 return s.to_i 64 end 65 def parse_key 66 c = self.next 67 if c =~ /['"]/ 68 self.back 69 return self.parse_string 70 end 71 if c !~ /[a-zA-Z]/ 72 error 'Invalid key token' 73 end 74 s = c 75 while self.has_next? 76 c = self.next 77 if c == ':' || WHITE_SPACES.include?(c) 78 self.back 79 return s 80 elsif c !~ /[a-zA-Z0-9_]/ 81 error 'Invalid key token' 82 end 83 s += c 84 end 85 error 'Invalid key token' 86 end 87 def parse_string 88 b = self.next 89 s = '' 90 while self.has_next? 91 c = self.next 92 case c 93 when '\' 94 c = self.next 95 case c 96 when '\', '/' 97 s += c 98 when 'b' 99 s += "\b" 100 when 'f' 101 s += "\f" 102 when 'n' 103 s += "\n" 104 when 'r' 105 s += "\r" 106 when 't' 107 s += "\t" 108 when 'u' 109 u = 0 110 while self.has_next? 111 c = self.next 112 i = HEX_LETTERS.index(c.downcase) 113 if i == nil 114 self.back 115 break 116 end 117 u = u * 16 | i 118 end 119 if u < 0x80 120 s += u.chr 121 elsif u < 0x800 122 s += (0xc0 | (u >> 6)).chr 123 s += (0x80 + (u & 0x3f)).chr 124 elsif u < 0x10000 125 s += (0xe0 | (u >> 12)).chr 126 s += (0x80 | ((u >> 6) & 0x3f)).chr 127 s += (0x80 | (u & 0x3f)).chr 128 elsif u < 0x200000 129 s += (0xf0 | (u >> 18)).chr 130 s += (0x80 | ((u >> 12) & 0x3f)).chr 131 s += (0x80 | ((u >> 6) & 0x3f)).chr 132 s += (0x80 | (u & 0x3f)).chr 133 elsif u < 0x4000000 134 s += (0xf8 | (u >> 24)).chr 135 s += (0x80 | ((u >> 18) & 0x3f)).chr 136 s += (0x80 | ((u >> 12) & 0x3f)).chr 137 s += (0x80 | ((u >> 6) & 0x3f)).chr 138 s += (0x80 | (u & 0x3f)).chr 139 else 140 s += (0xfc | (u >> 30)).chr 141 s += (0x80 | ((u >> 24) & 0x3f)).chr 142 s += (0x80 | ((u >> 18) & 0x3f)).chr 143 s += (0x80 | ((u >> 12) & 0x3f)).chr 144 s += (0x80 | ((u >> 6) & 0x3f)).chr 145 s += (0x80 | (u & 0x3f)).chr 146 end 147 else 148 error 'Invalid string token' 149 end 150 when b 151 return s 152 else 153 s += c 154 end 155 end 156 error 'Invalid string token' 157 end 158 def parse_object 159 self.next 160 o = {} 161 while self.has_next? 162 self.skip_white 163 c = self.next 164 if c == '}' 165 self.next 166 break 167 end 168 self.back 169 k = self.parse_key 170 self.skip_white 171 c = self.next 172 if c != ':' 173 error 'Expected ":" but not found' 174 end 175 self.skip_white 176 v = self.parse_value 177 o[k] = v 178 self.skip_white 179 c = self.current 180 if c == '}' 181 self.next 182 break 183 end 184 if c != ',' 185 error 'Expected "," or "}" but not found' 186 end 187 self.next 188 end 189 o 190 end 191 def parse_array 192 self.next 193 a = [] 194 while self.has_next? 195 self.skip_white 196 if self.current == ']' 197 break 198 end 199 i = self.parse_value 200 self.skip_white 201 c = self.next 202 a << i 203 if c == ']' 204 break 205 end 206 if c != ',' 207 error 'Expected "," or "]" but not found' 208 end 209 end 210 a 211 end 212 def parse_value 213 self.skip_white 214 c = self.current 215 case c 216 when '{' 217 return self.parse_object 218 when '[' 219 return self.parse_array 220 when '"', '\'' 221 return self.parse_string 222 when '0','1','2','3','4','5','6','7','8','9','-' 223 return self.parse_number 224 when 't' 225 return self.parse_constant('true', true) 226 when 'f' 227 return self.parse_constant('false', false) 228 when 'n' 229 return self.parse_constant('null', nil) 230 else 231 error 'Invalid sequence' 232 end 233 end 234 end 235 def self.parse(text) 236 JSAN::Context.new(text).parse_value 237 end 238end

以下のコードでテスト出来ます。

ruby

1load 'jsan.rb' 2 3p JSAN.parse <<'EOS' 4{"foo": "bar"} 5EOS 6 7p JSAN.parse <<'EOS' 8{foo: "bar"} 9EOS 10 11p JSAN.parse <<'EOS' 12{'foo': 'bar'} 13EOS

※ライセンスは MIT です。

投稿2018/01/26 04:36

mattn

総合スコア5030

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mattn

2018/01/26 04:42

補足ですが、キー名は /^[a-zA-Z][a-zA-Z0-9_]/ に制限しています。
guest

0

ベストアンサー

「文字列やキーの区切りだけ違う(こともある)」データ

とやらがよく分かりませんが、
jsonっぽいデータならばフロースタイルのYAMLとして読み込める可能性は高いかなと思います。

require 'yaml' p YAML.load %({'foo': 'bar'}) # => {"foo"=>"bar"} p YAML.load %({foo: "bar"}) # => {"foo"=>"bar"}

投稿2018/01/26 04:57

編集2018/01/26 04:59
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mattn

2018/01/26 05:02

僕もこれ考えたのですが foo: 'bar' の様な、JSON like としては正しくない物まで通ってしまうのでどうかなーと思ってやめました。
maisumakun

2018/01/26 08:44

YAMLにこんな使い方もあったのですね(改行を削って読み込めばきれいに通りました)。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問