Премахване на елементи от JSON въз основа на условие в C#

Имам JSON низ, който искам да мога да променям в C#. Искам да мога да изтрия набор от данни въз основа на това, когато една от дъщерните стойности е определена стойност.

Вземете следното

 {
  "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“ съответства, например ако моят 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 записа в собствена задача с помощта на Task Parallel Library, извикващо работник, който ще обработва собствената си малка страница и ще върне изчистения 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
Хаха, понякога съм такъв глупак. Моята паста от моята dev VM беше остаряла. Това всъщност беше код от един от първите ми опити, който завърши с ужасен провал. Първата грешка очевидно е, че трябва да изброите и ["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