Вот странный. Мне дана непродуманная входная строка, представляющая собой список простых строк ИЛИ 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;
}
}
Есть лучшие предложения?