Отправка / анализ нескольких объектов JSON - PullRequest
16 голосов
/ 18 ноября 2010

У меня есть сервер Sinatra, который возвращает несколько объектов JSON из базы данных в потоковом режиме. Объекты будут выглядеть так:

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

но это неверный JSON. Я могу добавить хак в обработку событий Синатры (вручную вставляя пропущенные разделители массива), чтобы ответ выглядел следующим образом:

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

, который сейчас является действующим JSON, но этот метод неэлегатный. Есть ли способ сделать это на стороне клиента? По сути, я хочу, чтобы функция JavaScript считывала строку и использовала допустимый объект JSON, а затем возвращала мне объект JSON и оставшуюся часть строки, итеративно вызывался до тех пор, пока не будет использована вся строка.

Ответы [ 9 ]

14 голосов
/ 18 ноября 2010

Нативная функция JSON.parse() ожидает, что вся строка будет корректной JSON.Я не знаю о парсере, который потребляет только первый действительный объект , как вы хотите.В любом случае, людям действительно следует создавать действительный JSON.

Если вы знаете, что в каждой строке содержится один объект, вы можете просто разбить строку на строку с помощью функции split() и проанализировать каждую строку по отдельности.

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);
}

Вы также можете использовать немного манипуляции со строками, чтобы преобразовать каждую строку в элемент массива и проанализировать все это.

str = "["+str.replace(/\n/g, ",")+"]";
JSON.parse(str);
2 голосов
/ 25 июня 2017

Я бы сделал это:

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

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

Редактировать:

в качестве пользователя комментария тремби

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

var res = JSON.parse('[' + str.replace(/}{(?=([^"]*"[^"]*")*[^"]*$)/g, '},{') + ']');
2 голосов
/ 18 ноября 2010

Если строки JSON являются однострочными, вы можете сделать что-то вроде этого:

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);

Если нет, просто проигнорируйте мой ответ.

Надеюсь, это вам поможет,
Alin


Примечание: я пытался выполнить требование

По сути, я хочу иметь Функция JavaScript читает строку и потреблять допустимый объект JSON, а затем верни мне объект JSON и остаток строки, итеративно вызывается до всей строки потребляется.

Вот почему я не использовал .split("\n").

1 голос
/ 19 января 2011

o.string is json Object.

добавить строку типа "new" в массив объектов или несколько объектов json.

, например:

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]);
                }

надеюсь, что это позволит проанализировать несколько объектов json.

1 голос
/ 18 ноября 2010

Это может быть не самым эффективным, но должно выполнить работу.

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 = "";
    }
}

Если вы знаете, что между объектами есть символ новой строки, это становится намного проще.

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);
0 голосов
/ 14 октября 2018

Сегодня я написал для этого небольшой модуль и опубликовал его на NPM как json-multi-parse. Код доступен на Github .

Мое решение простое, но, по общему признанию, возможно хрупкое, поскольку оно опирается на сообщение об ошибке JSON.parse throws при разборе такой строки. Он использует номер позиции, указанный в ошибке (номер в «Неожиданном токене {в JSON в позиции xyz»), чтобы проанализировать все до этого, а затем повторить и проанализировать все после.

Однако, он не сломается из-за фигурных скобок в строках, как некоторые другие предлагаемые решения.

Вот простая версия кода, которая будет работать в Chrome и Node.

const ERROR_REGEX = /^Unexpected token { in JSON at position (\d+)$/;
function jsonMultiParse(input, acc = []) {
    if (input.trim().length === 0) {
        return acc;
    }
    try {
        acc.push(JSON.parse(input));
        return acc;
    } catch (error) {
        const match = error.message.match(ERROR_REGEX);
        if (!match) {
            throw error;
        }
        const index = parseInt(match[1], 10);
        acc.push(JSON.parse(input.substr(0, index)));
        return jsonMultiParse(input.substr(index), acc);
    }
}

Ситуация усложняется, если вы тоже хотите поддерживать Firefox, который выдает ошибку в формате, в котором указан номер строки и символ в этой строке. Модуль, который я связал выше, обрабатывает этот случай.

0 голосов
/ 01 сентября 2018

Я написал эту маленькую функцию JavaScript, которая позволяет вам анализировать любую строку для объектов Json.Это работает, проходя через каждого персонажа и принимая во внимание иерархию.Преимущество этого решения заключается в том, что вы можете получить все объекты Json текста, не зная, что их разделяет.

function evaluateJsonString(string){
    var start = string.indexOf('{');
    if(start == -1)
        return false;
    let hierarchy = 0;
    let characters = string.split('');
    let objects = [];
    for(var index = start; index < characters.length; index++){
        let char = characters[index];
        if(char == '{')
            hierarchy++;
        if(char == '}')
            hierarchy--;
        if(hierarchy == 0){
            objects.push(JSON.parse(characters.slice(start, index + 1).join('')));
            index = start = index + characters.slice(index, characters.length).indexOf('{') - 1;
            if(start == -1)
                break;
        }
    }
    return objects;
}

let result = evaluateJsonString('This is {"name": "John", "age": 32, "hobbies": ["sport", "programming"]} He goes to {"name": "University", "director": {"name": "Peter", "age": 66, "hobbies": ["drinking coffee"]}}');

console.log(result);
0 голосов
/ 12 июня 2018

Если поток данных предоставляет вам несколько объектов JSON в одной строке, вам нужно разделить их на массив:

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

const json = '[' + str.replace(/}\n?{/g, '},{') + ']';

JSON.parse(json).forEach((obj) => {
  console.log('a:', obj.a);
});
0 голосов
/ 18 июня 2016

Я написал java-конвертер (используя библиотеку Джексона), который превращает несколько объектов JSON в файле в правильный массив 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;
      }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...