2010-11-17 18 views
15

Tengo un servidor Sinatra que devuelve múltiples objetos JSON de la base de datos de forma continua. Los objetos se verían así:Envío/análisis de varios objetos JSON

{"a": 1, "b": 2, "c": 3} 
{"a": 4, "b": 5, "c": 6} 
... 

pero esto no es válido. Puedo añadir un corte en el procesamiento de eventos de Sinatra (inyectar manualmente los delimitadores de matriz que faltan) para que la respuesta se parece a:

[ 
{"a": 1, "b": 2, "c": 3} 
, {"a": 4, "b": 5, "c": 6} 
] 

que es JSON válida ahora, pero esta técnica es poco elegante. ¿Hay alguna forma de hacer este lado del cliente? Básicamente, lo que quiero es tener una función de JavaScript para leer una cadena y consumir un objeto JSON válido, y luego devolverme el objeto JSON y el resto de la cadena, llamándolos iterativamente hasta que se consuma toda la cadena.

+0

A menos que el JSON está formateado de manera que se puede dividir, esto será difícil. Como bien puede necesitar analizar el JSON para encontrar el final de un objeto en particular. Si siempre está publicando objetos individuales, sin objetos anidados, entonces podría usar las llaves {} para dividir las declaraciones individuales. – Orbling

+0

Sí, eso es lo que pensé. Y si tengo que ajustar el servidor para devolver el JSON de una manera dividida, entonces también podría formatearlo como una matriz. –

+0

He escrito un artículo sobre cómo podemos enviar múltiples objetos al navegador utilizando json http://tryconcepts.blogspot.in/2012/01/return-multiple-object-using-json-in.html – yashpal

Respuesta

12

El JSON.parse() función nativa de esperar toda la cadena JSON sea válida. No conozco un analizador que solo consuma el primer objeto válido que desee. Y la gente realmente debería estar produciendo JSON válido de todos modos.

Si sabe que hay un objeto por línea, simplemente puede dividir el hilo por línea usando la función split() y analizar cada línea individualmente.

var str = '{"a": 1, "b": 2, "c": 3}\n'+ 
      '{"a": 4, "b": 5, "c": 6}'; 

var strLines = str.split("\n"); 


for (var i in strLines) { 
    var obj = JSON.parse(strLines[i]); 
    console.log(obj.a); 
} 

También podría usar un poco de manipulación de cadenas para transformar cada línea en un elemento de matriz y analizar todo.

str = "["+str.replace(/\n/g, ",")+"]"; 
JSON.parse(str); 
+0

Acepto tener un JSON válido donde sea posible; esto fue un artefacto de cómo la clase de Respuesta de Rack puede iterar automáticamente sobre un 'Enumerable' para usted, lo que permite devolver objetos JSON de forma continua. Es desafortunado que el envoltorio de los objetos no sea más elegante. Gracias por la respuesta. –

+1

bueno, es posible tener una corriente de jsons entrando, i.E. http://oboejs.com/ y no tienen un delimitador \ n. por lo tanto, la pregunta inicial es justa y no tiene nada de malo. luchando aquí también cómo analizarlo mejor. probablemente algo como https://gist.github.com/jgornick/3786127? –

2

Si las cadenas JSON son de una sola línea que puede hacer algo como esto:

var splitPoint = remainingData.indexOf("\n"); 
var currentJSONStr = splitPoint > -1 ? remainingData.substr(0, splitPoint) : remainingData; 
remainingData = splitPoint > -1 ? remainingData.substr(splitPoint+1) : ''; 
var dataObj = youJSONDecodeFuncOrEval(currentJSONStr); 

Si no, simplemente ignorar mi respuesta.

espero que esto le ayuda,
Alin


Nota: Traté de cumplir con el requisito

Básicamente, lo que quiero es tener un JavaScript función de lectura de una cadena y consume un objeto JSON válido, y luego me devuelve el objeto JSON y el resto de la cadena, iterativamente siendo cal led hasta que se consuma toda la cadena .

esta es la razón por la que no usé .split("\n").

+0

Sí, no fue así Es un requisito estricto, estaba tratando de describir lo que la función sería capaz de hacer. Al final, todavía necesitamos una respuesta de cadena que tenga un delimitador claro, lo que significa que tendré que ajustar los resultados del servidor de todos modos. ¡Gracias! –

0

utilizando recursividad un poco (pero relativamente el mismo que @Alin y al igual que @Alin asumiendo caracteres New Line son el separador "\ n".

Por supuesto, si su mal cadena JSON codificado contiene nuevas líneas en las que no se espera no va a funcionar. Personalmente me gustaría fijar la fuente en lugar de ajustar continuamente los datos de origen en torno a los malos.

var jsonObject = []; 
function parseBadJSON(str){ 
    var parseStr = "", remainder = ""; 
    if(str.indexOf("\n") != -1){ 
     parseStr = str.substr(0,str.indexOf("\n")); 
     remainder = str.substr(str.indexOf("\n") + 1); 
     jsonObject.push($.parseJSON(parseStr)); 
     if(remainder !== ""){ 
      parseBadJSON(remainder); 
     } 
    } else if(str != ""){ 
     jsonObject.push($.parseJSON(str)); 
    } 
} 

// ejemplo

var badJSON = '{"a":1,"b":2,"c":3}\n{"a":4,"b":5,"c":6}'; 
parseBadJSON(badJSON); 
console.log(jsonObject); 
0

Este m no puede ser el más eficiente, pero debe hacer el trabajo.

var s = '{"a": 1, "b": 2, "c": 3}{"a": 4, "b": 5, "c": 6}'; 
var sTemp = ""; 
var aObjs = []; 
for(var i=0; i<s.length; ++i) 
{ 
    sTemp += s[i]; 
    if (s[i] == "}") 
    { 
     aObjs.push(JSON.parse(sTemp)); 
     sTemp = ""; 
    } 
} 

Si sabe que hay un nuevo carácter de línea entre los objetos, se vuelve mucho más simple.

var sBad = '{"a": 1, "b": 2, "c": 3}\n{"a": 4, "b": 5, "c": 6}'; 
var sGood = "[" + sBad.replace(/\n/g, ",") + "]"; 
var aObjs = JSON.parse(sGood); 
1

o.string is json Objeto.

agrega una cadena como "nueva" a una matriz de objetos o varios objetos json.

por ejemplo:

json object---- 

{"id":2,"method":"listWirings","params":{"language":"anonymousLanguage","name":"mytest","working":"{\"modules\":[{\"config\":{\"position\":[186,59],\"xtype\":\"WireIt.ImageContainer\"},\"name\":\"Start\",\"value\":{}},{\"config\":{\"position\":[188,265],\"xtype\":\"WireIt.ImageContainer\"},\"name\":\"Stop\",\"value\":{}}],\"properties\":{\"description\":\"gfd\",\"name\":\"gf\"},\"wires\":[{\"src\":{\"moduleId\":0,\"terminal\":\"_OUTPUT\"},\"tgt\":{\"moduleId\":1,\"terminal\":\"StpIn\"}}]}"},"version":"json-rpc-2.0"}new 

    var str = o.toString(); 
       var s = str.split("new"); 
       for (var i = 0; i < s.length-1; i++) 
       { 
        var r = YAHOO.lang.JSON.parse(s[i]); 
       } 

esperanza esta voluntad de analizar múltiples objetos JSON.

-1

me escribió un convertidor de Java (usando la biblioteca Jackson) que convierte múltiples objetos JSON en un archivo en el correcto matriz JSON:

import java.io.File; 
import com.fasterxml.jackson.core.JsonFactory; 
import com.fasterxml.jackson.core.JsonParser; 
import com.fasterxml.jackson.core.JsonToken; 
import com.fasterxml.jackson.databind.JsonNode; 
import com.fasterxml.jackson.databind.MappingJsonFactory; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.node.ArrayNode; 
import com.fasterxml.jackson.databind.node.JsonNodeFactory; 
import com.fasterxml.jackson.databind.node.ObjectNode; 

public class ParseJson { 
    ObjectMapper mapper = new ObjectMapper(); 

    public static void main(String[] args) throws Exception { 
     File file = new File(args[0]); 
     JsonNode jn = new Parser().parse(file); 

     System.out.println(jn.toString()); 
    } 

    private enum ParserState { 
     start, 
     object, 
     array, 
     field, 
     done 
    }; 

    private static class Parser { 

     public Parser() { 
     } 

     public JsonNode parse(File file) throws Exception { 
      JsonNodeFactory factory = JsonNodeFactory.instance; 
      JsonFactory mappingFactory = new MappingJsonFactory();   
      @SuppressWarnings("deprecation") 
     JsonParser jp = mappingFactory.createJsonParser(file); 

      int n = 0; 
      JsonNode result = null; 
      JsonNode jn; 

      while((jn = parseNode(jp, false)) != null) { 
       if(n == 0) { 
        result = jn; 
       } else if(n == 1) { 
        ArrayNode an = factory.arrayNode(); 
        an.add(result); 
        an.add(jn); 
        result = an; 
       } else if(n > 1) { 
        ArrayNode an = (ArrayNode)result; 
        an.add(jn); 
       } else { 
        throw new Exception("Unexpected parser state"); 
       } 
       n++; 
      } 

      return result; 
     } 

     private JsonNode parseNode(JsonParser jp, boolean current) throws Exception { 
      JsonNodeFactory factory = JsonNodeFactory.instance; 

      ParserState state = ParserState.start; 
      JsonNode result = null; 
      String fieldName = null; 

      JsonToken token = current ? jp.getCurrentToken() : jp.nextToken(); 

      for(; token != null; token = jp.nextToken()) { 

       // System.out.println("Token: "+token+": "+jp.getValueAsString()); 

       switch(token) { 
       /** 
       * NOT_AVAILABLE can be returned if {@link JsonParser} 
       * implementation can not currently return the requested 
       * token (usually next one), or even if any will be 
       * available, but that may be able to determine this in 
       * future. This is the case with non-blocking parsers -- 
       * they can not block to wait for more data to parse and 
       * must return something. 
       */ 
       case NOT_AVAILABLE: { 
        break; 
       } 

       /** 
       * START_OBJECT is returned when encountering '{' 
       * which signals starting of an Object value. 
       */ 
       case START_OBJECT: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.objectNode(); 
          state = ParserState.object; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = parseNode(jp, true); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = parseNode(jp, true); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break;     
       } 

       /** 
       * END_OBJECT is returned when encountering '}' 
       * which signals ending of an Object value 
       */ 
       case END_OBJECT: { 
        switch(state) { 
         case object: { 
          assert result != null; 
          assert fieldName == null; 

          state = ParserState.done; 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break;     
       } 

       /** 
       * START_ARRAY is returned when encountering '[' 
       * which signals starting of an Array value 
       */ 
       case START_ARRAY: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.arrayNode(); 
          state = ParserState.array; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = parseNode(jp, true); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = parseNode(jp, true); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break;     
       } 

       /** 
       * END_ARRAY is returned when encountering ']' 
       * which signals ending of an Array value 
       */ 
       case END_ARRAY: { 
        switch(state) { 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          state = ParserState.done; 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break;     
       } 

       /** 
       * FIELD_NAME is returned when a String token is encountered 
       * as a field name (same lexical value, different function) 
       */ 
       case FIELD_NAME: { 
        fieldName = jp.getValueAsString(); 
        switch(state) { 
         case object: { 
          assert result != null; 
          assert fieldName == null; 

          state = ParserState.field; 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       /** 
       * Placeholder token returned when the input source has a concept 
       * of embedded Object that are not accessible as usual structure 
       * (of starting with {@link #START_OBJECT}, having values, ending with 
       * {@link #END_OBJECT}), but as "raw" objects. 
       *<p> 
       * Note: this token is never returned by regular JSON readers, but 
       * only by readers that expose other kinds of source (like 
       * <code>JsonNode</code>-based JSON trees, Maps, Lists and such). 
       */ 
       case VALUE_EMBEDDED_OBJECT: { 
        throw new Exception("Token not supported: "+token); 
       } 

       /** 
       * VALUE_STRING is returned when a String token is encountered 
       * in value context (array element, field value, or root-level 
       * stand-alone value) 
       */ 
       case VALUE_STRING: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.textNode(jp.getValueAsString()); 
          state = ParserState.done; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = factory.textNode(jp.getValueAsString()); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = factory.textNode(jp.getValueAsString()); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       /** 
       * VALUE_NUMBER_INT is returned when an integer numeric token is 
       * encountered in value context: that is, a number that does 
       * not have floating point or exponent marker in it (consists 
       * only of an optional sign, followed by one or more digits) 
       */ 
       case VALUE_NUMBER_INT: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.numberNode(jp.getLongValue()); 
          state = ParserState.done; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = factory.numberNode(jp.getLongValue()); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = factory.numberNode(jp.getLongValue()); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       /** 
       * VALUE_NUMBER_INT is returned when a numeric token other 
       * that is not an integer is encountered: that is, a number that does 
       * have floating point or exponent marker in it, in addition 
       * to one or more digits. 
       */ 
       case VALUE_NUMBER_FLOAT: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.numberNode(jp.getDoubleValue()); 
          state = ParserState.done; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = factory.numberNode(jp.getDoubleValue()); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = factory.numberNode(jp.getDoubleValue()); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       /** 
       * VALUE_TRUE is returned when encountering literal "true" in 
       * value context 
       */ 
       case VALUE_TRUE: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.booleanNode(true); 
          state = ParserState.done; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = factory.booleanNode(true); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = factory.booleanNode(true); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       /** 
       * VALUE_FALSE is returned when encountering literal "false" in 
       * value context 
       */ 
       case VALUE_FALSE: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.booleanNode(false); 
          state = ParserState.done; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = factory.booleanNode(false); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = factory.booleanNode(false); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       /** 
       * VALUE_NULL is returned when encountering literal "null" in 
       * value context 
       */ 
       case VALUE_NULL: { 
        switch(state) { 
         case start: { 
          assert result == null; 
          assert fieldName == null; 

          result = factory.nullNode(); 
          state = ParserState.done; 
          break; 
         } 
         case field: { 
          assert result != null; 
          assert fieldName != null; 

          ObjectNode on = (ObjectNode)result; 
          JsonNode jn = factory.nullNode(); 
          on.set(fieldName, jn); 
          fieldName = null; 
          state = ParserState.object; 
          break; 
         } 
         case array: { 
          assert result != null; 
          assert fieldName == null; 

          ArrayNode an = (ArrayNode)result; 
          JsonNode jn = factory.nullNode(); 
          an.add(jn); 
          break; 
         } 
         default: { 
          throw new Exception("Unexpected state: "+state+", for token: "+token); 
         } 
        } 

        break; 
       } 

       default: { 
        throw new Exception("Token not supported: "+token); 
       } 

       } 

       if(state == ParserState.done) { 
        break; 
       } 
      }   

      return result; 
     } 
    } 
} 
2

Me gustaría hacer esto:

var str = '{"a": 1, "b": 2, "c": 3}{"a": 4, "b": 5, "c": 6}'; 

var res = JSON.parse('[' + str.replace(/}{/g, '},{') + ']'); 
Cuestiones relacionadas