Как извлечь несколько объектов json из строки и сохранить другой текст в javascript

#javascript

#javascript

Вопрос:

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

Пример строки, для которой мне может потребоваться выполнить это, выглядит следующим образом…

 beforetext{"Message":"The request is invalid.","ModelState":{"Id":["Unknown 
contract"]}}middletext{"Message":"The request is invalid.","ModelState": 
{"Id":["Unknown contract"]}}aftertext
  

Я добился некоторого прогресса, выполнив поиск { и }, и с помощью индексов я смог извлечь и разрезать строку, пока не получил свои объекты json и другой текст.

Проблема в том, что объект json может содержать несколько символов { и }, как в примере выше, и я не могу точно определить, где один объект окончательно начинается и заканчивается.

Как только я извлеку json и текст между ними, я буду выглядеть так..

 beforetext
{
  "Message": "The request is invalid.",
  "ModelState": {
    "Id": [
      "Unknown contract"
    ]
  }
}
middletext
{
  "Message": "The request is invalid.",
  "ModelState": {
    "Id": [
      "Unknown contract"
    ]
  }
}
aftertext
  

Я изучил регулярное выражение, но не смог найти четкого решения.

Есть идеи?

Комментарии:

1. Если вы можете быть уверены, что before / middle / after-text не содержит никаких {} символов и не содержит никаких строковых значений json, вы можете вести подсчет вашей «глубины» в объекте json — каждый раз, когда вы сталкиваетесь с { , увеличивайте свой счетчик, каждый раз, когда вы сталкиваетесь с } , уменьшайтеваш счетчик. Когда ваш счетчик достигает 0, вы получаете полный объект JSON. Если вы считаете, что ваш текст может содержать эти символы, вам нужно пройтись по строке, отслеживая, в каком состоянии в данный момент находится курсор. (См sites.google.com/site/gson/streaming )

2. Спасибо @AndrewRueckert. Я смог реализовать решение (добавленное ниже), основанное на этой идее, которое соответствует моим потребностям.

Ответ №1:

Это не так уж сложно разобрать самостоятельно, если вы можете разумно предсказать, с какой строкой вы будете иметь дело. Вы можете сохранить стек своих разделителей, и когда стек достигает или отклоняется от нулевой длины, вы знаете, что находитесь на границе. Вы можете использовать строки как целые единицы, чтобы они могли содержать что угодно (кроме " ).

Это предполагает, что строки и контейнеры сбалансированы, но нетрудно добавить базовую проверку ошибок в этот синтаксический анализатор. Я изменил ваши данные, чтобы добавить еще несколько трудностей (например, начиная с [ и включая {} в строки)

 let s = 'beforetext[{"Message":"The request is {invalid.}"},{"ModelState":{"Id":["Unknown contract"]}}]middletext{"Message":"The request is invalid.","ModelState": {"Id":["Unknown contract"]}}aftertext'

function balance(s){
    let opens = ['{', '['], closes = ['}', ']'],  // define opening and closing delimiters
    res = [], current = '', stack = []

    for (let i = 0; i<s.length; i  ){
        let char = s[i]
        if (char == '"'){                         // take strings as units to allow them to contain delimters
            let next = s.indexOf('"', i 1)
            current  = s.substring(i, next 1)
            i = next 
        }
        else if(opens.includes(char)) {           // new opening, push to stack
            if (stack.length == 0){
                res.push(current)
                current = char
            }
            stack.push(char) 
        } else if (closes.includes(char)) {       // new closing pop
            stack.pop()
            if (stack.length == 0) {
                res.push(current   char)
                current = ''
            }
        }
        else {current  = char}
    }
    res.push(current)
    return res
}

console.log(balance(s).join('nn'))  

Ответ №2:

Мне удалось решить эту проблему, реализовав предложение Эндрю Рюкерта

  • Я перебрал каждый символ в строке.
  • Если я столкнулся с ‘{‘, я увеличил глубину на 1. Если это ‘}’, я уменьшил глубину на 1.
  • Всякий раз, когда глубина была равна 0, я знал, что это мои начальный и конечный индексы для json.
  • Я сохранил текст до начального индекса и json как объект. Затем я перенес эти свойства в массив.
  • Некоторая дополнительная логика для учета конца строки, где остается текст, но нет json, и если строка не содержит никаких ‘{‘ или ‘}’.

Пример…

 jsonObjects() 
{
  var jsonString = this.value;
  var jsonObjects = [];
  var remainingString = '';
  var depth = 0;
  var indexStart = 0;
  var indexEnd = 0;
  var pointer = 0;

  if (!jsonString.includes('{') amp;amp; !jsonString.includes('}')) {
    jsonObjects.push({ "pretext": jsonString, "json": null });
  }
  else {
    for (var i = 0; i < jsonString.length; i  ) 
    {
      if (jsonString.charAt(i) === '{') {
        if (depth === 0) {
          indexStart = i;
        }
        depth  ;
      }
      else if (jsonString.charAt(i) === '}') {
        depth--;
        if (depth === 0) {
          indexEnd = i;

          var finalJson = JSON.parse(jsonString.substring(indexStart, indexEnd   1));
          var gapText = jsonString.substring(pointer, indexStart);
          jsonObjects.push({ "pretext": gapText, "json": finalJson });

          pointer = indexEnd   1;
          remainingString = jsonString.substring(pointer, jsonString.length);

          if (!remainingString.includes('{') amp;amp; !remainingString.includes('}')) {
            jsonObjects.push({ "pretext": remainingString, "json": null });
          }
        }
      }
    }
  }
  return jsonObjects;   
}
  

При некотором условном форматировании в vue результат выглядит довольно красиво..

 <div v-for="jsonObject in jsonObjects" v-bind:key="jsonObject.pretext"> 
  <p>{{ jsonObject.pretext }}</p>
  <vue-json-pretty :deep="1" v-if="jsonObject.json != null" :data="jsonObject.json"></vue-json-pretty>   
</div>
  

введите описание изображения здесь

Ответ №3:

Я не уверен, хотите ли вы все три в качестве группы, но если нет, вы можете разделить, как показано ниже. Это даст вам массив со всеми объектами json.

 let yourString = 'beforetext{"Message":"The request is invalid.","ModelState":{"Id":["Unknown contract"]}}middletext{"Message":"The request is invalid.","ModelState": {"Id":["Unknown contract"]}}aftertext'

yourString.split(/beforetextb|middletextb|aftertextb/).filter(json=>json)