2009-03-25 5 views
12

Je suis perplexe avec ce problème.Comment gérez-vous le conflit entre ActiveSupport :: JSON et la gemme JSON?

ActiveSupport::JSON définit to_json sur divers objets de base, ainsi que la gemme JSON. Cependant, l'implémentation n'est pas la même - la version ActiveSupport prend des arguments et la version gem JSON ne le fait pas.

J'ai installé une gemme qui nécessitait la gemme JSON et mon application s'est brisée. Le problème est que j'utilise to_json dans un contrôleur qui renvoie une liste d'objets, mais je veux contrôler quels attributs sont retournés.

Lorsque le code n'importe où dans mon système ne require 'json' je reçois ce message d'erreur:

TypeError: wrong argument type Hash (expected Data)

J'ai essayé quelques choses que je lis en ligne pour le corriger, mais rien ne fonctionnait. J'ai fini par réécrire la gemme pour utiliser ActiveSupport::JSON.decode au lieu de JSON.parse.

Cela fonctionne, mais ce n'est pas durable ... Je ne peux pas forcer des gemmes chaque fois que je veux utiliser une gemme qui nécessite la gemme JSON.

Mise à jour: La meilleure solution de ce problème consiste à mettre à niveau vers Rails 2.3 ou version ultérieure, qui l'a corrigé.

+1

Pourquoi cette question marquée comme « communauté wiki »? –

+0

Je ne sais pas, j'ai juste pensé que j'essaierais ça et que je verrais ce que ça fait. –

+0

J'ai senti votre douleur, j'espère que ce désordre sera trié un jour – MatthewFord

Répondre

2

Mise à jourCette correction s'applique uniquement aux rails < 2.3. Comme Giles le mentionne plus bas, ils ont corrigé ceci en 2.3 en interne en utilisant à peu près la même technique. Mais méfiez-vous de la précédente tentative de compatibilité de Rails à la compatibilité de Rails (json/add/rails), qui, si nécessaire explicitement va tout casser à nouveau.

Voulez-vous dire que l'instruction require 'json' elle-même déclenche cette exception? Ou voulez-vous dire quand vous appelez @something.to_json(:something => value) vous obtenez l'erreur? Ce dernier est ce que je m'attendais, si vous avez un problème nécessitant la gemme JSON alors je ne suis pas sûr de ce qui se passe.

Je viens de rencontrer ce problème avec la gemme oauth. Dans mon cas, il n'y a pas de véritable conflit, car la gem oauth ne dépend pas de l'implémentation to_json. Par conséquent, le problème est que JSON est en train d'écraser les déclarations ActiveSupport. J'ai résolu cela en demandant simplement json avant que ActiveSupport soit chargé. Mettre

require 'json' 

à l'intérieur du Rails::Initializer a fait l'affaire (bien que le mettre après le bloc n'a pas).

Cela permet à ActiveSupport de contourner l'implémentation JSON par défaut à la place.

Maintenant si vous utilisez une gemme qui dépend réellement de l'implémentation JSON de to_json alors vous êtes dans un ruisseau. C'est certainement le pire de la méta-programmation, et je recommanderais aux développeurs de gemmes Rails et JSON de résoudre le conflit, même si cela sera douloureux car l'un ou l'autre devra casser la compatibilité ascendante. À court terme, les auteurs de gem peuvent être en mesure de combler le vide en prenant en charge les deux implémentations. Ceci est plus ou moins faisable en fonction de la façon dont la gemme utilise la méthode. Le pire scénario est une fourche officielle (c'est-à-dire gem et gem-rails).

+0

Ouais, je veux dire que quand j'utilise quelque chose que les appels exigent 'json' ça balaie la version Rails de to_json en me causant beaucoup de douleur. Merci pour vos suggestions. Avez-vous eu de la chance avec l'option suggérée d'utiliser require 'json/add/rails'? Je ne peux pas le faire fonctionner. –

+0

ne fonctionne que la première fois que vous l'appelez, donc si vous l'appelez avant que la version de Rails ne soit chargée, elle ne fera rien quand le plugin l'exigera. J'ai vérifié cela en pratique, placez-le dans le bloc Rails :: Initializer. Aucune idée de la chose json/add/rails, mais je ne pense pas que ce soit nécessaire. – gtd

0

Je suis sûr qu'ils ont corrigé cela en 2.3 mais je ne me souviens plus comment.

+0

Oui, je pense qu'ils se sont débarrassés d'appeler to_json directement. Au lieu de cela, vous définissez as_json ou vous appelez JSON.generate ou ActiveSupport :: JSON.encode directement. Et avec le nouveau backend JSON, je pense que ActiveSupport :: JSON.encode utilisera votre bibliothèque préférée. –

0

Je n'ai pas encore essayer, mais il semble que Rails 2.3.3 vous donne un certain contrôle:

ActiveSupport::JSON.backend = 'JSONGem' 

Found here

+0

Oui, Rails 2.3+ résout ce problème. –

0

Dans mon cas unique mais, j'ai eu un Ruby (non rails) application qui a effectivement chargé une application Rails (à partir d'une charge de config/environment.rb) ainsi que quelques gemmes qui référencent json. Cela m'a causé d'énormes maux de tête en raison du fait que je ne pouvais pas simplement modifier le fichier environment.rb de l'application Rails. J'ai fini par forcer un certain nombre de gemmes afin de faire fonctionner json sans élever le redouté TypeError: type d'argument erroné Message de hachage (Données attendues).

J'ai eu un peu de chance avec cette solution, ce qui est exactement le contraire que la réponse du wiki de la communauté ci-dessus ... http://blog.swivel.com/code/2009/03/active-support-and-json-gems-dont-play-nice.html qui préconise essentiellement d'appeler require 'active_support' AVANT require 'json'

C'était la seule façon de le faire fonctionner et croyez-moi, j'ai tout essayé pendant plusieurs mois.

18

MISE À JOUR: Même avec Rails 3.2, le même problème n'est toujours pas résolu. Le hack méchant pour charger de force la gem json et l'écraser, c'est.

Finalement, je me suis retrouvé avec le code suivant, pour contourner complètement to_json ActiveSupport complètement. Mettez-le dans config/initializers/patches.rb, et vous pouvez faire {}.jsonize ou [].jsonize pour générer une chaîne JSON. Aucun conflit avec quoi que ce soit, garanti.

# Undo the effect of 'active_support/core_ext/object/to_json' 
require 'json' 
[Object, Array, Hash].each do |klass| 
    klass.class_eval <<-RUBY, __FILE__, __LINE__ 
    def jsonize(options = nil) 
     ::JSON.generate self, :quirks_mode => true 
    end 
    RUBY 
end 

Les 8 lignes de code font votre application 50 fois plus rapide pour l'encodage JSON. Probablement que vous voulez faire la même chose. :)


J'ai eu un problème similaire jusqu'à Rails 2.3.8.

Le problème est que ActiveSupport::JSON.backend = 'JSONGem' est une solution à demi-assertion et vous devez toujours remplacer certains encodeurs. (AVERTISSEMENT:. 3.x Rails, qui utilise MultiJson, il doit être ActiveSupport::JSON.backend = :json_gem au moins, ou il sera en silence no-op)

Dans mon cas, je devais remplacer String#to_json parce que gemme JSON 1.4. 3 est meilleur car il n'encombre pas aveuglément les caractères non-ascii-mais-valides-UTF8 sous la forme de "\uXXXX" où ce n'est pas nécessaire, donc vous obtenez des octets plus courts (bon pour la sérialisation) et des résultats faciles à lire ("日本語" regarde beaucoup plus sexy à mes yeux que "\u65e5\u672c\u8a9e").

est ici le patch de singe que je me sers - mettre le code suivant dans config/initializers/patches.rb

module ActiveSupport 
    module JSON 
    module Encoding 
     class << self 
     def escape(string) 
      ::JSON.generate([string])[1..-2] 
     end 
     end 
    end 
    end 
end 

et vous êtes libre d'utiliser to_json sur quoi que ce soit - Chaîne, Array et Hash.

+0

Cela a fonctionné avec la fixation d'Emoji dans Rails 3.1.3. Merci. – Chalkers

3

Après avoir combattu pendant un certain temps .. Je trouve la solution la plus simple d'être:

if defined?(ActiveSupport::JSON) 
    [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass| 
    klass.class_eval do 
    def to_json(*args) 
     super(args) 
    end 
    def as_json(*args) 
     super(args) 
    end 
    end 
    end 
end 

put que partout après activesupport est chargé ..