Разделите список через запятую, который содержит несколько простых строк, а некоторые JSON - PullRequest
0 голосов
/ 18 февраля 2020

Вот странный. Мне дана непродуманная входная строка, представляющая собой список простых строк ИЛИ JSON двоичных объектов, разделенных запятыми. Например:

string input = "{<some JSON object>},Normal Text,Some-Other-String-Without-Commas,{JSON_3},...,{JSON_n}"

И я должен разбить это на два списка - список JSON строк и список не JSON строк.

Приятно то, что известно, что не JSON строки не содержат специальных символов (без запятых и фигурных скобок, которые могут быть приняты за JSON). Неприятная вещь заключается в том, что JSON капли (все из которых начинаются с { и заканчиваются }), очевидно, будут содержать множество запятых.


«Очевидное» решение (используя String.Split):

List<string> split = input.Split(',').ToList();

, конечно, не сможет избежать запятых, присутствующих в самих JSON объектах ({})


Я рассматривал ручной подход - обход строки по буквам и разделение нового элемента только в том случае, если число { равно количеству }. Что-то вроде:

List<string> blobs = new List<string>();
int start = 0, nestingLevel = 0;
for (int i = 0; i < input.Length; i++)
{
    if (input[i] == '{') nestingLevel++;
    else if (input[i] == '}') nestingLevel--;
    else if (input[i] == ',' && nestingLevel == 0)
    {
        blobs.Add(input.Substring(start, i - start));
        start = i + 1;
    }
}
// Trivial TODO: split blobs into JSON and non-JSON by checking if the first character is '{'

(Примечание: выше определенно содержит ошибки)

Этот подход, вероятно, не справляется с множеством вещей, которые могут появиться в JSON. Например, символы { и } могут «мягко» появляться в JSON, если они экранированы внутри строки (кавычки) - но если я начну считать кавычки, я могу встретить экранированные кавычки (* 1030) *), который должен быть , а не . Но если я проверю на наличие побегных персонажей, мне лучше убедиться, что они сами не сбежали (\\) - что за кошмар. Я бы предпочел не писать сам полноценный парсер JSON.


Я также подумал о добавлении скобок JSON массива на любом конце строки ([]) и разрешении JSON сериализатор десериализует его как массив JSON, а затем повторно сериализует каждый из элементов массива по одному:

List<string> JsonBlobs = Newtonsoft.Json.Linq.JArray.Parse("[" + input + "]").Select(t => t.ToString()).ToList();

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

Я предполагаю, что идеальное решение должно быть гибридом между двумя вышеупомянутыми решениями. На ум приходит следующее чудовище:

List<string> blobs = new List<string>();
int start = 0;
bool in_json_land = false;
for (int i = 0; i < input.Length; i++)
{
    if (input[i] == '{') in_json_land = true;
    else if (input[i] == '}' and in_json_land) {
        try {
            JToken blob = Newtonsoft.Json.Parse(input.Substring(start, i - start));
            blobs.Add(blob.ToString());
            start = i + 1;
        } catch { /* Must not have encountered the end of the JSON yet... */ }
    }
    else if (input[i] == ',' && !in_json_land)
    {
        blobs.Add(input.Substring(start, i - start));
        start = i + 1;
    }
}

Есть лучшие предложения?

...