Удаление элементов из JSON на основе условия в C#

У меня есть строка JSON, которую я хочу изменить на С#. Я хочу иметь возможность удалить набор данных, когда одно из дочерних значений является определенным значением.

Возьмите следующее

 {
  "responseHeader":{
    "status":0,
    "QTime":0,
    "params":{
      "explainOther":"",
      "fl":"*,score",
      "indent":"on",
      "start":"0",
      "q":"*:*",
      "hl.fl":"",
      "qt":"",
      "wt":"json",
      "fq":"",
      "version":"2.2",
      "rows":"2"}
  },
  "response":{"numFound":2,"start":0,"maxScore":1.0,"docs":
  [{
        "id":"438500feb7714fbd9504a028883d2860",
        "name":"John",
        "dateTimeCreated":"2012-02-07T15:00:42Z",
        "dateTimeUploaded":"2012-08-09T15:30:57Z",
        "score":1.0
   },
   {
        "id":"2f7661ae3c7a42dd9f2eb1946262cd24",
        "name":"David",
        "dateTimeCreated":"2012-02-07T15:02:37Z",
        "dateTimeUploaded":"2012-08-09T15:45:06Z",
        "score":1.0
    }]
 }}

Выше показаны два результата ответа. Я хочу иметь возможность удалить всю группу результатов родительского ответа, когда ее дочернее значение «id» совпадает, например, если мой идентификатор был «2f7661ae3c7a42dd9f2eb1946262cd24», я бы хотел, чтобы вторая группа была удалена, и поэтому мой результат будет выглядеть следующим образом .

{
  "responseHeader":{
    "status":0,
    "QTime":0,
    "params":{
      "explainOther":"",
      "fl":"*,score",
      "indent":"on",
      "start":"0",
      "q":"*:*",
      "hl.fl":"",
      "qt":"",
      "wt":"json",
      "fq":"",
      "version":"2.2",
      "rows":"2"}},
  "response":{"numFound":2,"start":0,"maxScore":1.0,"docs":[
  {
        "id":"438500feb7714fbd9504a028883d2860",
        "name":"John",
        "dateTimeCreated":"2012-02-07T15:00:42Z",
        "dateTimeUploaded":"2012-08-09T15:30:57Z",
        "score":1.0
    }]
  }}

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

Любая помощь очень ценится.


person andyJ    schedule 20.11.2012    source источник
comment
Он настолько большой, что вы будете беспокоиться о том, чтобы не потерять всю память, если прочитаете весь поток JSON сразу?   -  person tmesser    schedule 20.11.2012
comment
Это хороший крик, но, скорее всего, результаты будут перенаправлены на что-то более управляемое, например, 100 результатов на странице.   -  person andyJ    schedule 20.11.2012
comment
Будет ли когда-нибудь ситуация, когда вам придется откатывать страницу, чтобы найти что-то, что нужно удалить? Не похоже, что это так, но я хочу быть уверен.   -  person tmesser    schedule 20.11.2012
comment
Нет, не нужно было бы откатывать   -  person andyJ    schedule 20.11.2012


Ответы (4)


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

        var jObj = (JObject)JsonConvert.DeserializeObject(json);
        var docsToRemove = new List<JToken>();
        foreach (var doc in jObj["response"]["docs"])
        {
            var id = (string)doc["id"];
            if (knownIds.Contains(id))
            {
                docsToRemove.Add(doc);
            }
            else
            {
                knownIds.Add(id);
            }
        }
        foreach (var doc in docsToRemove)
            doc.Remove();

Кажется, это хорошо работает с паршивым маленьким консольным приложением, которое я развернул для тестирования, но мое тестирование было ограничено примерами данных выше, поэтому, если есть какие-либо проблемы, оставьте комментарий, чтобы я мог их исправить.

Что бы это ни стоило, это будет в основном работать в линейном времени относительно того, сколько элементов вы ему подаете, что, вероятно, тем больше алгоритмической производительности, которую вы собираетесь получить, не получая веселья от этой проблемы. На ум приходит превращение каждой страницы из ~100 записей в свою собственную задачу с использованием библиотеки параллельных задач, вызывающей работника, который будет обрабатывать свою собственную маленькую страницу и возвращать очищенную строку JSON. Это, безусловно, сделало бы это быстрее, если бы вы запустили его на многоядерной машине, и я был бы рад предоставить некоторый код, чтобы вы начали с этого, но это также огромный перепроектирование для масштаба проблемы, как она представлена.

person tmesser    schedule 20.11.2012
comment
Как вы тестировали свой код? doc["id"] выдает исключение Cannot access child value on Newtonsoft.Json.Linq.JProperty. - person L.B; 20.11.2012
comment
Хм, странно. Позвольте мне попытаться воспроизвести, посмотреть, не пропустил ли я что-то глупое, когда вставлял код. - person tmesser; 20.11.2012
comment
Ха-ха, иногда я такой тупой. Моя вставка из моей виртуальной машины устарела. На самом деле это был код одной из моих первых попыток, которая закончилась ужасным провалом. Первая ошибка, очевидно, заключается в том, что вы также должны перечислять ["docs"]. Другая ошибка, скрывающаяся в этом первом фрагменте кода, заключается в том, что вы не можете модифицировать IEnumerable, пока вы его перечисляете. Мне потребовалось некоторое время, чтобы еще раз взглянуть на мой ответ, чтобы понять свою ошибку. Обновлено сейчас! - person tmesser; 20.11.2012

Ни один из приведенных выше ответов не сработал для меня, мне пришлось Remove() ребенка от Parent (.Parent.Remove()), а не просто Remove() его. Пример рабочего кода ниже:

namespace Engine.Api.Formatters
{
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Formatting;
    using System.Net.Http.Headers;
    using System.Threading.Tasks;
    using System.Web.Script.Serialization;
    using System.Xml;
    using System.Xml.Serialization;

    public class ReducedJson
    {
        public dynamic WriteToStreamAsync(object value)
        {
                    var json = new JavaScriptSerializer().Serialize(value);
                    var serializedJson = (JObject)JsonConvert.DeserializeObject(json);
                    foreach (var response in serializedJson["ProductData"]["Motor"]["QuoteResponses"])
                    {
                        response["NetCommResults"].Parent.Remove();
                        foreach (var netCommResult in response["BestPriceQuote"]["NetCommResults"])
                        {
                            netCommResult["Scores"].Parent.Remove();
                        }
                    }

          return serializedJson;
        }
}

Надеюсь, это сэкономит вам время.

person Matas Vaitkevicius    schedule 05.09.2016

Я просто нахожу другой ответ.

var aJson = JsonConvert.DeserializeObject<JObject>(json);
var doc = aJson["response"]["docs"];
JObject docs = new JObject();
docs["docs"] = doc;

// remove
docs.SelectTokens(string.Format("docs[?(@.id == '{0}')]", "2f7661ae3c7a42dd9f2eb1946262cd24")).ToList().ForEach(i => i.Remove());
// replace
aJson.SelectToken("response.docs").Replace(docs["docs"]);
person JoseRamos    schedule 30.10.2018

person    schedule
comment
OP надеется сделать это для любого заданного идентификатора, поэтому вам придется вести список известных идентификаторов и последовательно оценивать каждый элемент, удаляя их, если List.Contains(id) возвращается верно... но опять же, это предполагает, что элемент достаточно мал, чтобы простой вызов DeserializeObject не вызывал OutOfMemoryException! - person tmesser; 20.11.2012
comment
@YYY Я могу расширить ответ, чтобы легко удалить несколько идентификаторов, но ваш комментарий о OOM - это всего лишь ваше предположение. Вы создаете свои собственные проблемы и ожидаете, что я отвечу на них. Я только вижу в вопросе, что может быть тысячи результатов. - person L.B; 20.11.2012
comment
Э-э, я не понимаю вашей враждебности - если вы проверите вопрос, вы заметите, что я пытаюсь прояснить ограничения памяти, чтобы я мог попытаться дать свой собственный ответ. Когда говорят, что есть тысячи строк, это разумный вопрос. - person tmesser; 20.11.2012