10301


Rails4 + Grape で開発を進めています。

下記のようなヘッダ情報を上手くパースする方法はありますでしょうか。

[code]

    X-Hoge-Authorization: Val1="test", Val2="aaa", Val3=1234
    
[/code]

headers['X-Hoge-Authorization']で、中身自体は取得できるのですが、  
当然ただの「Val1="test", Val2="aaa", Val3=1234」というStringになっています。  
それをさらに

[code]

    {
      Val1: "test",
      Val2: "aaa",
      Val3: 1234
    }
    
[/code]

という感じにパースするには自分で正規表現等を書くしかないのでしょうか。  
パーサーなどが用意されているようでしたら教えて頂ければと思います。


CC BY-SA 3.0
2015-05-16T05:01:19.660
10251
2015-05-19T00:50:46.890
2015-05-16T09:43:54.973
8000
7352
question
ruby-on-rails grape
HTTPヘッダに書かれた Val1="test", Val2="aaa",... といった文字列をパースしたい


なさそうなので、簡単に書いてみました。

[code]

    #!/usr/bin/env ruby
    
    def parser(s)
      Hash[ s.split(/\s*,\s*/).map { |e| e.split(/\s*=\s*/) } ]
    end
    
    p parser('Val1="test", Val2="aaa", Val3 = 1234')
    
[/code]

こんな感じでどうでしょうか？


CC BY-SA 3.0
2015-05-18T05:10:40.883
10301
2015-05-18T05:10:40.883
7900
10251
answer


非標準のHTTPヘッダのフォーマットは上位プロトコルが任意に定めるものなので、汎用的なパーサなどというものは存在し得ません。逆に言えば、そのヘッダを定めているプロトコルを処理するライブラリであればパーサを必ず持っているはずです。

質問ではプロトコルが伏せられているため具体的には指摘できませんが、そのプロトコルを処理するライブラリを探してください。

なおパーサを自力で実装する場合は、必ず仕様を確認しましょう。識別子の文字種や、文字列中の`"`や`,`は許容されるのか、許容される場合どのようにエスケープされるのか、エンコーディングはどうなっているのか、など確認しておかないと、簡単な正規表現で実装出来ると思っていたら思わぬデータが来たときに破綻します。


CC BY-SA 3.0
2015-05-18T12:38:16.083
10325
2015-05-19T00:11:04.367
2015-05-19T00:11:04.367
5793
5793
10251
answer


質問文で書かれているとおり、keyをシンボルに、valueは文字列と数値で型を分ける、という形でアウトプットするなら、こういう書き方もできます。

[code]

    s = 'Val1="test", Val2="aaa", Val3=1234'
    s.scan(/(\w+)=("[^"]*"|\d+)/)
      .map{|k,v| [k.to_sym, v[/(?<=^")(.*)(?="$)/] || v.to_i]}
      .to_h
    # => {:Val1=>"test", :Val2=>"aaa", :Val3=>1234}
    
[/code]

ただこれ以外の入力パターン（例: Val4=1.23, Val-5="12\"34" など）があった場合はさらに工夫が必要になると思います。  
以上、ご参考までに。


CC BY-SA 3.0
2015-05-18T22:29:59.323
10337
2015-05-18T22:29:59.323
85
10251
answer


指摘ばかりだと面白くないので僕も書いてみました。トークンのパースは JSON
に頼りました。なのでバックスラッシュによるエスケープも可能ですし、浮動小数点のパースも可能です。

[code]

    require 'json'
    
    def parse(line)
      mx = /^\s*(\w+)\s*=\s*([0-9.]+|"(?:\\.|[^"]+)*?")\s*,?\s*/
      mv = {}
      l = line
      until l.empty? do
        m = l.match(mx)
        unless m
          raise "parse error at pos #{line.length - l.length}"
        end
        mv[m[1]] = JSON.parse("{\"dummy\": #{m[2]}}")["dummy"]
        l = l[m[0].length..-1]
      end
      mv
    end
    
    p parse('Val1="te\nst", Val2="aaa", Val3 = 1234.5')
    # {"Val1"=>"te\nst", "Val2"=>"aaa", "Val3"=>1234.5}
    
[/code]

仕様として必要ないのであれば文字列は両端の `"` を削るだけでいいです。ただしその場合、JSON
を使わないので浮動小数点のサポートが無くなります。(作れば出来ますが)  
※JSON を使うなら少し弄れば true/false のパースも出来るかと思います。  
尚、トークンの解析に失敗した場合は例外が発生する様にしてあります。  
以下は JSON を使わないバージョンです。

[code]

    def parse(line)
      mx = /^\s*(\w+)\s*=\s*([0-9.]+|"(?:\\.|[^"]+)*?")\s*,?\s*/
      mv = {}
      l = line
      until l.empty? do
        m = l.match(mx)
        unless m
          raise "parse error at pos #{line.length - l.length}"
        end
        mv[m[1]] = m[2][0] == '"' ? m[2][1..-2] : m[2].to_i
        l = l[m[0].length..-1]
      end
      mv
    end
    
    p parse('Val1="test", Val2="aaa", Val3 = 1234')
    # {"Val1"=>"test", "Val2"=>"aaa", "Val3"=>1234}
    
[/code]


CC BY-SA 3.0
2015-05-19T00:21:54.190
10346
2015-05-19T00:50:46.890
2015-05-19T00:50:46.890
440
440
10251
answer