[rubysur] problema con json y unicode chars

207 views
Skip to first unread message

Alvaro Olivencia

unread,
Aug 28, 2012, 9:59:13 AM8/28/12
to rubysur
Hola gente,
tengo un problema generando json desde datos guardados en my db.
El problema es que el json no es valido debido a unicode chars.
Alguien sabe como solucionar este problema ?

Estoy usando ruby 1.8.7, rails 2.3.8

Gracias.

Alvaro Olivencia

unread,
Aug 28, 2012, 8:44:31 PM8/28/12
to rubysur
Luego de un poco más de debug me encontré que el problema es con el encode.
El origen en parte del problema, es que en mi app los users copian y pegan directamente desde Word en mi campo description.

Entonces, cuando chequeo el output generado desde mi controller veo:
"Museum Ship (Vigad\ufffd Square) entrance", como parte de mi description. La parte  \ufffd es la que está rompiendo mi json.

Mi controller es:
respond_to do |format|
        format.html
        format.json
        format.js {
          render :json => {
              :type => "tours",
              :html=> render_to_string(:partial => 'results', :locals => {:results =>   
              @services})

          }
        }
end

Encontré esta solución (o al menos creo que aplica a este caso) : http://stackoverflow.com/questions/4583924/string-force-encoding-in-ruby-1-8-7-or-rails-2-x

Les parece esta solución correcta o debería estar haciendo otra cosa ?

Gracias.






2012/8/28 Alvaro Olivencia <alvaroo...@gmail.com>

Luis Lavena

unread,
Aug 28, 2012, 8:47:00 PM8/28/12
to rub...@googlegroups.com

Si estás en 1.8.7 podes usar iconv para convertir el encoding a algo que json entienda.

AFAIK json debería entender utf-8, pero no siempre anda.

Sorry for top posting. Sent from mobile.

Alvaro Olivencia

unread,
Aug 28, 2012, 9:45:44 PM8/28/12
to rub...@googlegroups.com


2012/8/28 Luis Lavena <luisl...@gmail.com>


Si, estoy usando 1.8.7 y rails 2.3.8
Creo que usé correctamente iconv pero no me dió resultado, posiblemente los parametros que estoy usando están mal ?:

ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
@services.each do |s|
   s.description = ic.iconv(s.description + ' ')[0..-2]
end

Pero me sigue generando "Museum Ship (Vigad\ufffd Square) entrance"
Si uso iconv se supone que debería saber el encoding que estoy utilizando, no ?

Muchas gracias.

Alvaro Olivencia

unread,
Aug 28, 2012, 10:49:44 PM8/28/12
to rub...@googlegroups.com
Otro dato más, chequeando el tab JSON de FF veo:
Kossuth Museum Ship (Vigad� Square)

Ese unicode me está matando :)




2012/8/28 Alvaro Olivencia <alvaroo...@gmail.com>

Luis Lavena

unread,
Aug 29, 2012, 6:01:35 AM8/29/12
to rub...@googlegroups.com

Tsl vez porque no es una letra sino un símbolo de control?

http://www.fileformat.info/info/unicode/char/fffd/index.htm

Sorry for top posting. Sent from mobile.

Alvaro Olivencia

unread,
Aug 29, 2012, 8:46:55 AM8/29/12
to rub...@googlegroups.com
Así es luis.
De todas formas, al no ser un ducho en encoding (solo lo muy basico), todo este tema me tiene algo confundido.
Primero que nada: Cómo es que fue a para ese símbolo a mi db ? O sea, la respuesta es fácil: fue un user que lo ingresó por medio de un form, pero esto me lleva a otra pregunta: debería remover todos estos chars ¨raros¨ antes de insertar en la db ?

Ahora bien, en este momento tengo que solucionar este problema teniendo en cuenta que varios user pudieron haber hecho lo mismo.
En una primera instancia, si es posible, preferiría no correr una query que remueva todos estos simbolos raros ya que no quiero modificar datos de los usuarios.

Entonces me gustaría resolver la cosa por medio de ruby (o algun método mágico de rails). Como antes dije, estoy usando ruby 1.8.7 y rails 2.3.8, mi tabla está en utf8.
Intenté hacer algo como:

ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
valid_string = ic.iconv(untrusted_string + ' ')[0..-2]
Source : http://po-ru.com/diary/fixing-invalid-utf-8-in-ruby-revisited/

Pero no tuve suerte, sigue devolviendo \ufffd

Otros datos:
  • Puedo acceder correctamente al string por medio del browser, o sea, mostrando el record del user en el browser y todo funciona bien, supongo yo por el encoding utf8 que tengo seteado en rails.
  • Sin embargo cuando intento obtener el record por medio de JSON el output de json está mal formado.
  • Otra cosa que me confunde es el FF, cuando chequeo los tabs console > response, veo el famoso ¨blablabla \ufffd blablablab¨, pero cuando chequeo  console > JSON, veo: ¨blablabla blablablab¨. Aguna idea de porque se ven las dos versiones ?
  • Está bien que   sea convertido a \ufffd ?

PS: aparte de existen otros unicode raros que hacen que el json se rompa.
PS2: ahora no estoy en mi casa y no puedo probarlo, pero les parece que alguna de las opciones que se dan aqui me pueda solucionar el dolor de cabeza? : http://stackoverflow.com/questions/7010565/ruby-1-8-7-how-to-get-rid-of-non-printable-chars-while-scraping

Muchas gracias !

2012/8/29 Luis Lavena <luisl...@gmail.com>

Luis Lavena

unread,
Aug 29, 2012, 9:27:51 AM8/29/12
to rub...@googlegroups.com
2012/8/29 Alvaro Olivencia <alvaroo...@gmail.com>:
> Así es luis.

Hola Alvaro, esta vez respondo desde mi maquina, asi puedo darte una
mejor respuesta.

> De todas formas, al no ser un ducho en encoding (solo lo muy basico), todo
> este tema me tiene algo confundido.

No sos el unico, encoding es un gran tema, y ni que hablar de las
inconsistencias de Ruby 1.9 sobre el tema... rant para otro email..

> Primero que nada: Cómo es que fue a para ese símbolo a mi db ? O sea, la
> respuesta es fácil: fue un user que lo ingresó por medio de un form, pero
> esto me lleva a otra pregunta: debería remover todos estos chars ¨raros¨
> antes de insertar en la db ?

Copy y paste de un documento de Word, es en muchos de los casos el
origen de los caracteres raros.

Este tema (aunque con otro non-printable symbol de unicode) se dio en
varios proyectos, y despues de analizar la mayoria son generados en
Word en Windows y non-English (muchas veces en instalaciones en aleman
y frances).

>
> Ahora bien, en este momento tengo que solucionar este problema teniendo en
> cuenta que varios user pudieron haber hecho lo mismo.
> En una primera instancia, si es posible, preferiría no correr una query que
> remueva todos estos simbolos raros ya que no quiero modificar datos de los
> usuarios.
>
> Entonces me gustaría resolver la cosa por medio de ruby (o algun método
> mágico de rails). Como antes dije, estoy usando ruby 1.8.7 y rails 2.3.8, mi
> tabla está en utf8.
> Intenté hacer algo como:
>
> ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
> valid_string = ic.iconv(untrusted_string + ' ')[0..-2]
> Source : http://po-ru.com/diary/fixing-invalid-utf-8-in-ruby-revisited/
>
> Pero no tuve suerte, sigue devolviendo \ufffd,
>

En primer momento, tenes que ver que podes hacer para vos reproducir
el problema, ignorando los datos existentes.

For ejemplo, en la consola de Rails:

>> a = "Museum Ship (Vigad\xEF\xBF\xBD Square) entrance"
=> "Museum Ship (Vigad? Square) entrance"
>> puts a
Museum Ship (Vigad? Square) entrance
=> nil
>> a.to_xs
=> "Museum Ship (Vigad&#65533; Square) entrance"

Ahora tengo el string como esta en el DB, Ruby 1.8.7 no entiende de
encoding, asi que vamos a ver de suprimir cualquier cosa que no sea
UTF-8 y que sean caracteres de control.

> Otros datos:
>
> Puedo acceder correctamente al string por medio del browser, o sea,
> mostrando el record del user en el browser y todo funciona bien, supongo yo
> por el encoding utf8 que tengo seteado en rails.

Si te fijas en el HTML que hace el output y te fijas de usar h para
sanitizar la salida, vas a ver que ese caracter se imprime como
&#xfffd; (o &#65533;)

> Sin embargo cuando intento obtener el record por medio de JSON el output de
> json está mal formado.

Por que no es UTF-8

> Otra cosa que me confunde es el FF, cuando chequeo los tabs console >
> response, veo el famoso ¨blablabla \ufffd blablablab¨, pero cuando chequeo
> console > JSON, veo: ¨blablabla � blablablab¨. Aguna idea de porque se ven
> las dos versiones ?

Fijate arriba, \ufffd es la respuesta que obtenes pero segun la info
de Unicode, en realidad son 3 bytes (EF BF BD).

> Está bien que � sea convertido a \ufffd ?
>

Si, esa es la representacion:

http://www.fileformat.info/info/unicode/char/fffd/browsertest.htm

>
> PS: aparte de existen otros unicode raros que hacen que el json se rompa.

Puff, un monton.

> PS2: ahora no estoy en mi casa y no puedo probarlo, pero les parece que
> alguna de las opciones que se dan aqui me pueda solucionar el dolor de
> cabeza? :
> http://stackoverflow.com/questions/7010565/ruby-1-8-7-how-to-get-rid-of-non-printable-chars-while-scraping
>

Si, algo similar hicimos en uno de nuestros proyectos.

Fijate que:

>> a.bytes.collect
=> [77, 117, 115, 101, 117, 109, 32, 83, 104, 105, 112, 32, 40, 86,
105, 103, 97, 100, 239, 191, 189, 32, 83, 113, 117, 97, 114, 101, 41,
32, 101, 110, 116, 114, 97, 110, 99, 101]

Y usando ASCII//TRANSLIT//IGNORE:

>> Iconv.iconv("ASCII//TRANSLIT//IGNORE", "UTF-8", a).first.bytes.collect
=> [77, 117, 115, 101, 117, 109, 32, 83, 104, 105, 112, 32, 40, 86,
105, 103, 97, 100, 32, 83, 113, 117, 97, 114, 101, 41, 32, 101, 110,
116, 114, 97, 110, 99, 101]

La secuencia de bytes 239, 191, 189 (\ufffd) desaparace.

Pero es importante que tengas cuidado ya que ASCII//TRANSLIT va a
sacar los umlauts (dieresis).

Ejemplo: ä

http://www.fileformat.info/info/unicode/char/E4/index.htm

>> b = "L\xC3\xA3use"
=> "Lãuse"
>> b.to_xs
=> "L&#227;use"
>> b.bytes.collect
=> [76, 195, 163, 117, 115, 101]
>> Iconv.iconv("ASCII//TRANSLIT//IGNORE", "UTF-8", b).first.bytes.collect
=> [76, 126, 97, 117, 115, 101]
>> Iconv.iconv("ASCII//TRANSLIT//IGNORE", "UTF-8", b)
=> ["L~ause"]

Saludos,
--
Luis Lavena
AREA 17
-
Perfection in design is achieved not when there is nothing more to add,
but rather when there is nothing more to take away.
Antoine de Saint-Exupéry

Alvaro Olivencia

unread,
Aug 29, 2012, 10:16:03 AM8/29/12
to rub...@googlegroups.com
Wow.. muchas gracias por tu ayudar Luis, lamentablemente como te dije ahora no puedo testear nada de lo que me escribiste.
Inline:

2012/8/29 Luis Lavena <luisl...@gmail.com>

2012/8/29 Alvaro Olivencia <alvaroo...@gmail.com>:
> Así es luis.

Hola Alvaro, esta vez respondo desde mi maquina, asi puedo darte una
mejor respuesta.

> De todas formas, al no ser un ducho en encoding (solo lo muy basico), todo
> este tema me tiene algo confundido.

No sos el unico, encoding es un gran tema, y ni que hablar de las
inconsistencias de Ruby 1.9 sobre el tema... rant para otro email..


Y peor aún, por lo que leí en ruby 1.8.7 es aún peor !

 

> Primero que nada: Cómo es que fue a para ese símbolo a mi db ? O sea, la
> respuesta es fácil: fue un user que lo ingresó por medio de un form, pero
> esto me lleva a otra pregunta: debería remover todos estos chars ¨raros¨
> antes de insertar en la db ?

Copy y paste de un documento de Word, es en muchos de los casos el
origen de los caracteres raros.


Así parece. Para que los users no sigan insertando estos unicode en la bd, te parece de crear un parser que remueva todos estos chars cuando el user intenta insertar el record ?
Filosoficamente tal vez es ¨malo¨ ya estaría modificando datos del user.. pero bueno...ellos se lo buscaron :p, los ¨malos¨ son ellos.



Este tema (aunque con otro non-printable symbol de unicode) se dio en
varios proyectos, y despues de analizar la mayoria son generados en
Word en Windows y non-English (muchas veces en instalaciones en aleman
y frances).


Scheisse, warum die Leute benützten Word !

 

>
> Ahora bien, en este momento tengo que solucionar este problema teniendo en
> cuenta que varios user pudieron haber hecho lo mismo.
> En una primera instancia, si es posible, preferiría no correr una query que
> remueva todos estos simbolos raros ya que no quiero modificar datos de los
> usuarios.
>
> Entonces me gustaría resolver la cosa por medio de ruby (o algun método
> mágico de rails). Como antes dije, estoy usando ruby 1.8.7 y rails 2.3.8, mi
> tabla está en utf8.
> Intenté hacer algo como:
>
> ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
> valid_string = ic.iconv(untrusted_string + ' ')[0..-2]
> Source : http://po-ru.com/diary/fixing-invalid-utf-8-in-ruby-revisited/
>
> Pero no tuve suerte, sigue devolviendo \ufffd,
>

En primer momento, tenes que ver que podes hacer para vos reproducir
el problema, ignorando los datos existentes.

Ok, no entiendo bien que queres decir con ¨ignorando¨ en este caso. Tal vez reproducir el problema abstrayéndome de la db ?
 

For ejemplo, en la consola de Rails:

>> a = "Museum Ship (Vigad\xEF\xBF\xBD Square) entrance"
=> "Museum Ship (Vigad? Square) entrance"
>> puts a
Museum Ship (Vigad? Square) entrance
=> nil
>> a.to_xs
=> "Museum Ship (Vigad&#65533; Square) entrance"

Ahora tengo el string como esta en el DB,

Cuando chequeo el valor en mysql workbench veo ¨Museum Ship (Vigad� Square)¨, pero tal vez lo que estoy viendo es algo visual y no lo REAL, y como vos decis, lo real o ¨como está en la DB¨ es ¨Museum Ship (Vigad&#65533 Square)¨
O sea, te entendí bien y quisiste decir que el valor real que está en la db es  "Museum Ship (Vigad&#65533; Square) entrance" ?


 
Ruby 1.8.7 no entiende de
encoding, asi que vamos a ver de suprimir cualquier cosa que no sea
UTF-8 y que sean caracteres de control.

> Otros datos:
>
> Puedo acceder correctamente al string por medio del browser, o sea,
> mostrando el record del user en el browser y todo funciona bien, supongo yo
> por el encoding utf8 que tengo seteado en rails.

Si te fijas en el HTML que hace el output y te fijas de usar h para
sanitizar la salida, vas a ver que ese caracter se imprime como
&#xfffd; (o &#65533;)


bue.. buen dato, o sea que si no veo &#xfffd; o &#65533; significa que no estoy escapeando la salida y por lo tanto ¨regalado¨ a XSS !
Ok, si, quitar los umlauts no es la idea.
 

http://www.fileformat.info/info/unicode/char/E4/index.htm

>> b = "L\xC3\xA3use"
=> "Lãuse"
>> b.to_xs
=> "L&#227;use"
>> b.bytes.collect
=> [76, 195, 163, 117, 115, 101]
>> Iconv.iconv("ASCII//TRANSLIT//IGNORE", "UTF-8", b).first.bytes.collect
=> [76, 126, 97, 117, 115, 101]
>> Iconv.iconv("ASCII//TRANSLIT//IGNORE", "UTF-8", b)
=> ["L~ause"]

Saludos,


Bueno, muchas gracias por los consejos, luego en casa pruebo.
Reply all
Reply to author
Forward
0 new messages