2009-03-25 6 views
12

Sono perplesso con questo problema.Come gestite il conflitto tra ActiveSupport :: JSON e la gemma JSON?

ActiveSupport::JSON definisce to_json su vari oggetti core e così fa la gemma JSON. Tuttavia, l'implementazione non è la stessa: la versione di ActiveSupport accetta argomenti e la versione gemma JSON no.

Ho installato una gemma che richiedeva la gemma JSON e la mia app si è rotta. Il problema è che sto usando to_json in un controller che restituisce un elenco di oggetti, ma voglio controllare quali attributi vengono restituiti.

Quando il codice ovunque nel mio sistema fa require 'json' ottengo questo messaggio di errore:

TypeError: wrong argument type Hash (expected Data)

ho provato un paio di cose che ho letto on-line per risolvere il problema, ma niente ha funzionato. Ho finito per riscrivere la gemma per utilizzare ActiveSupport::JSON.decode anziché JSON.parse.

Questo funziona ma non è sostenibile ... Non posso incastonare gemme ogni volta che voglio usare una gemma che richiede la gemma JSON.

Aggiornamento: La migliore soluzione di questo problema è l'aggiornamento a Rails 2.3 o versioni successive, che lo ha risolto.

+1

Perché questa domanda contrassegnati come "comunità wiki"? –

+0

Non lo so, ho appena pensato di provarlo e vedere cosa fa. –

+0

Ho sentito il tuo dolore, spero che questo pasticcio venga risolto un giorno – MatthewFord

risposta

2

AggiornamentoQuesta correzione è applicabile solo a Rails < 2.3. Come Giles menziona sotto, hanno risolto questo in 2.3 internamente usando più o meno la stessa tecnica. Ma fai attenzione al precedente tentativo della gemma JSON sulla compatibilità di Rails (json/add/rails), che, se richiesto, spezzerà tutto di nuovo.

Intendi dire che l'istruzione require 'json' solleva quell'eccezione? O intendi quando chiami @something.to_json(:something => value) ottieni l'errore? Quest'ultimo è quello che mi aspetterei, se hai un problema che richiede la gemma JSON, allora non sono sicuro di cosa sta succedendo.

Mi sono appena imbattuto in questo problema con la gemma oauth. Nel mio caso, non c'è un vero conflitto, perché la gemma oauth non dipende dall'implementazione to_json. Pertanto, il problema è che JSON sta clobberando le dichiarazioni di ActiveSupport. Ho risolto questo problema richiedendo semplicemente json prima che ActiveSupport sia caricato. Mettendo

require 'json' 

all'interno del Rails::Initializer ha fatto il trucco (anche se metterlo dopo il blocco non ha fatto).

Ciò consente ad ActiveSupport di clobare invece l'implementazione JSON predefinita.

Ora se si sta utilizzando una gemma che in realtà dipende dall'implementazione JSON di to_json, si è in un torrente. Questo è sicuramente il peggiore dei metaprogrammi, e vorrei difendere gli sviluppatori di gemme Rails e JSON per risolvere il conflitto, anche se sarà doloroso perché uno o l'altro dovrà rompere la compatibilità all'indietro.

A breve termine, gli autori di gem possono essere in grado di colmare il divario sostenendo entrambe le implementazioni. Questo è più o meno fattibile a seconda di come la gemma usa il metodo. Uno scenario peggiore è un fork ufficiale (ad esempio gem e gem-rails).

+0

Sì, intendo dire che quando uso qualcosa che chiama richiede "json", la versione di Rails di to_json mi causa molto dolore. Grazie per i vostri suggerimenti. Hai avuto fortuna con l'opzione suggerita di usare require 'json/add/rails'? Non riesco a farlo funzionare. –

+0

richiede che funzioni solo la prima volta che lo chiami, quindi se lo chiami prima che la versione di Rails sia caricata non farà nulla quando il plugin lo richiede. L'ho verificato in pratica, inserendolo nel blocco Rails :: Initializer. Nessuna idea riguardo la cosa json/add/rails, ma non penso sia necessario. – gtd

0

Sono abbastanza sicuro che hanno risolto questo in 2.3 ma non riesco a ricordare come.

+0

Sì, penso che si siano liberati di chiamare direttamente to_son. Invece si definisce as_json o si chiama direttamente JSON.generate o ActiveSupport :: JSON.encode. E con il nuovo materiale di backend JSON, penso che ActiveSupport :: JSON.encode userà la tua libreria preferita. –

0

devo ancora provare, ma sembra che Rails 2.3.3 ti dà un certo controllo:

ActiveSupport::JSON.backend = 'JSONGem' 

Found here

+0

Sì, Rails 2.3+ risolve questo problema. –

0

Nel mio caso unico anche se, ho avuto un rubino (non rails) che ha effettivamente caricato un'app Rails (da un carico config/environment.rb) e alcune gemme che hanno fatto riferimento a json. Ciò mi ha causato grossi grattacapi a causa del fatto che non potevo semplicemente modificare il file environment.rb dell'app Rails. Ho finito con la forking di un certo numero di gemme per far funzionare json senza sollevare il temuto TypeError: tipo di argomento errato Hash (previsto Data) messaggio.

ho avuto una certa fortuna con questa soluzione, che è esattamente il contrario, come la risposta del wiki comunità di sopra ... http://blog.swivel.com/code/2009/03/active-support-and-json-gems-dont-play-nice.html che fondamentalmente sostenitori chiamando richiedono 'active_support' PRIMA richiedono 'json'

Questo era l'unico modo per farlo funzionare, e credimi, ho provato tutto per molti mesi.

18

UPDATE: Anche con Rails 3.2, lo stesso problema rimane non risolto. Il cattivo trucco per caricare forzatamente la gemma JSON e sovrascriverlo, cioè.

Alla fine ho finito con il seguente codice, per bypassare completamente l'to_json di ActiveSupport. Inseriscilo in e puoi creare {}.jsonize o [].jsonize per generare una stringa JSON. Nessun conflitto con nulla, garantito.

# 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 

Le 8 linee di codice rendere il vostro app 50 volte più veloce per la codifica JSON. Probabilmente vuoi fare lo stesso. :)


Ho avuto un problema simile fino a Rails 2.3.8.

Il problema è che ActiveSupport::JSON.backend = 'JSONGem' è una soluzione half-assed ed è comunque necessario sovrascrivere alcuni encoder. (ATTENZIONE:. Per Rails 3.x, che utilizza MultiJson, deve essere ActiveSupport::JSON.backend = :json_gem almeno, o sarà in silenzio no-op)

Nel mio caso, avevo bisogno di sovrascrivere String#to_json perché JSON gioiello 1.4. 3 è migliore in quanto non codifica ciecamente caratteri non ASCII-MAI-validi in formato "\uXXXX" dove non è necessario, quindi ottieni byte più brevi (buoni per la serializzazione) e risultati di facile lettura ("日本語" sembra molto più sexy per i miei occhi di "\u65e5\u672c\u8a9e").

Ecco la patch scimmia che ho usato - ha messo il seguente codice nel 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 

e siete liberi di utilizzare to_json su qualsiasi cosa - String, Array e Hash.

+0

Questo ha funzionato con il fissaggio di Emoji in Rails 3.1.3. Grazie. – Chalkers

3

Dopo aver combattuto per un po '.. Ho trovato la soluzione più semplice per essere:

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 che ovunque dopo ActiveSupport viene caricato ..