53 votes

Impossible d'utiliser la syntaxe dot pour le hash ruby

Je utilise net/http pour extraire des données json de l'API Yahoo Placemaker. Après avoir reçu la réponse, j'effectue JSON.parse sur la réponse. Cela me donne un hash qui ressemble à:

{"processingTime"=>"0.001493", "version"=>"1.4.0.526 build 111113", "documentLength"=>"25", "document"=>{"administrativeScope"=>{"woeId"=>"2503863", "type"=>"City", "name"=>"Tampa, FL, US", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}}, "geographicScope"=>{"woeId"=>"2503863", "type"=>"City", "name"=>"Tampa, FL, US", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}}, "localScopes"=>{"localScope"=>{"woeId"=>"2503863", "type"=>"City", "name"=>"Tampa, FL, US (City)", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}, "southWest"=>{"latitude"=>"27.8132", "longitude"=>"-82.6489"}, "northEast"=>{"latitude"=>"28.1714", "longitude"=>"-82.2539"}, "ancestors"=>[{"ancestor"=>{"woeId"=>"12587831", "type"=>"County", "name"=>"Hillsborough"}}, {"ancestor"=>{"woeId"=>"2347568", "type"=>"State", "name"=>"Florida"}}, {"ancestor"=>{"woeId"=>"23424977", "type"=>"Country", "name"=>"États-Unis"}}]}}, "extents"=>{"center"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}, "southWest"=>{"latitude"=>"27.8132", "longitude"=>"-82.6489"}, "northEast"=>{"latitude"=>"28.1714", "longitude"=>"-82.2539"}}, "placeDetails"=>{"placeId"=>"1", "place"=>{"woeId"=>"2503863", "type"=>"City", "name"=>"Tampa, FL, US", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}}, "placeReferenceIds"=>"1", "matchType"=>"0", "weight"=>"1", "confidence"=>"8"}, "referenceList"=>{"reference"=>{"woeIds"=>"2503863", "placeReferenceId"=>"1", "placeIds"=>"1", "start"=>"15", "end"=>"20", "isPlaintextMarker"=>"1", "text"=>"Tampa", "type"=>"plaintext", "xpath"=>""}}}}

Je peux accéder aux éléments en faisant des choses comme jsonResponse['version'] mais je ne peux pas faire jsonResponse.version. Pourquoi?

0 votes

Je ne pense pas que Ruby prenne en charge cela.

102voto

steenslag Points 29662

Hash n'a pas de syntaxe avec des points pour ses clés. OpenStruct en a :

require 'ostruct'
hash = {:name => 'John'}
os = OpenStruct.new(hash)
p os.name #=> "John"

REMARQUE : Ne fonctionne pas avec des hachages imbriqués.

1 votes

Seulement pour un niveau de points os.name.firstname ne semble pas fonctionner, os.name renvoie un hash normal

1 votes

Cela ne fonctionne pas pour les hachages et les tableaux imbriqués. Voir la réponse de whodabudda ci-dessous.

44voto

whodabudda Points 361

OpenStruct fonctionnera bien pour un hachage pur, mais pour des hachages avec des tableaux intégrés ou d'autres hachages, la syntaxe pointée échouera. J'ai trouvé cette solution, qui fonctionne bien sans charger un autre gemme : https://coderwall.com/p/74rajw/convert-a-complex-nested-hash-to-an-object Les étapes de base sont :

data = YAML::load(File.open("votre fichier yaml"))
json_data = data.to_json
mystr = JSON.parse(json_data,object_class: OpenStruct)

vous pouvez maintenant accéder à tous les objets dans mystr en utilisant la syntaxe pointée.

3 votes

"Puisque à la fois JSON et OpenStruct sont dans la bibliothèque standard de Ruby, nous n'aurons pas de dépendances tierces."

13voto

steel Points 793

Les hachages Ruby ne fonctionnent pas nativement de cette manière, mais le gemme HashDot le permettrait.

HashDot permet l'utilisation d'une syntaxe à point sur les hachages. Il fonctionne également sur les chaînes JSON qui ont été re-analysées avec JSON.parse.

require 'hash_dot'

hash = {b: {c: {d: 1}}}.to_dot
hash.b.c.d => 1

json_hash = JSON.parse(hash.to_json)
json_hash.b.c.d => 1

0 votes

Belle pierre précieuse! Merci!

8voto

megas Points 10549

Pourquoi pas, vous pouvez le faire via la méta-programmation

module LookLikeJSON
  def method_missing(meth, *args, &block)
    if has_key?(meth.to_s)
      self[meth.to_s]
    else
      raise NoMethodError, 'méthode non définie #{meth} pour #{self}' 
    end
  end
end

h = {"processingTime"=>"0.001493", "version"=>"1.4.0.526 build 111113", "documentLength"=>"25"}
h.extend(LookLikeJSON)
h.processingTime #=> "0.001493"

0 votes

Interpoler une chaîne en apostrophe ? : / Et en utilisant stringify_keys! pour normaliser d'abord les clés.

0 votes

Peut-être une version plus petite et sur une seule ligne de ceci : Hash.define_method(:method_missing) { |*args| m = args.first; fetch(m, nil) || fetch(m.to_s, nil) || super(*args) }

6voto

d11wtq Points 17790

Ceci est une fonctionnalité JavaScript, pas une fonctionnalité Ruby. En Ruby, pour utiliser une "syntaxe point", l'objet devrait répondre à ces méthodes. Les hachages Ruby utilisent la méthode #[](clé) pour accéder aux éléments.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X