2015-10-08 16 views
8

Ich habe eine JObject, die als Vorlage für den Aufruf RESTful Web-Services verwendet wird. Diese JObject wird über einen Parser erstellt und da es als Vorlage verwendet wird, die dem Benutzer sagt, wie das Endpoint-Schema aussieht, musste ich eine Möglichkeit finden, alle Eigenschaften beizubehalten. Deshalb setze ich ihre Werte auf null. Wie als Beispiel, ist es das, was ursprünglich das Objekt wie folgt aussieht:JSON.NET serialisieren JObject beim Ignorieren Null Eigenschaften

{ 
    "Foo":{ 
     "P1":null, 
     "P2":null, 
     "P3":null, 
     "P4":{ 
     "P1":null, 
     "P2":null, 
     "P3":null, 
     }, 
     "FooArray":[ 
     { 
      "F1":null, 
      "F2":null, 
      "F3":null, 
     } 
     ] 
    }, 
    "Bar":null 
} 

Der Benutzer dann in der Lage ist, in einzelne Felder zu füllen, wie sie benötigen, wie Foo.P2 und Foo.P4.P1:

{ 
    "Foo":{ 
     "P1":null, 
     "P2":"hello world", 
     "P3":null, 
     "P4":{ 
     "P1":1, 
     "P2":null, 
     "P3":null, 
     }, 
     "FooArray":[ 
     { 
      "F1":null, 
      "F2":null, 
      "F3":null, 
     } 
     ] 
    }, 
    "Bar":null 
} 

bedeutet, dass sie nur kümmern sich um diese beiden Bereiche. Jetzt möchte ich diese Vorlage (JObject) in eine JSON-Zeichenfolge serialisieren, möchte aber nur die Felder, die ausgefüllt werden sollen. Also habe ich es versucht:

string json = JsonConvert.SerializeObject(template, 
    new JsonSerializerSettings 
    { 
     NullValueHandling = NullValueHandling.Ignore 
    }); 

Leider hat dies nicht funktioniert. Ich bin auf this question gestoßen und habe festgestellt, dass ein null Wert im Objekt ein tatsächlicher JToken Typ ist und nicht wirklich ein null, was Sinn macht. In diesem speziellen Fall muss ich jedoch diese "unbenutzten" Felder loswerden können. Ich habe versucht, manuell über Knoten zu iterieren und sie zu entfernen, aber das hat auch nicht funktioniert. Beachten Sie, dass der einzige verwaltete Typ, den ich verwende, JObject ist; Ich habe kein Modell, um das Objekt zu konvertieren oder Attribute zu definieren, da dieses "Template" zur Laufzeit aufgelöst wird. Ich habe mich nur gefragt, ob jemand auf ein Problem wie dieses gestoßen ist und irgendwelche Einsichten hat. Jede Hilfe wird sehr geschätzt!

Antwort

11

Sie können eine rekursive Hilfsmethode wie die folgende verwenden, um die null Werte aus Ihrer JToken Hierarchie zu entfernen, bevor Sie sie serialisieren.

using System; 
using Newtonsoft.Json.Linq; 

public static class JsonHelper 
{ 
    public static JToken RemoveEmptyChildren(JToken token) 
    { 
     if (token.Type == JTokenType.Object) 
     { 
      JObject copy = new JObject(); 
      foreach (JProperty prop in token.Children<JProperty>()) 
      { 
       JToken child = prop.Value; 
       if (child.HasValues) 
       { 
        child = RemoveEmptyChildren(child); 
       } 
       if (!IsEmpty(child)) 
       { 
        copy.Add(prop.Name, child); 
       } 
      } 
      return copy; 
     } 
     else if (token.Type == JTokenType.Array) 
     { 
      JArray copy = new JArray(); 
      foreach (JToken item in token.Children()) 
      { 
       JToken child = item; 
       if (child.HasValues) 
       { 
        child = RemoveEmptyChildren(child); 
       } 
       if (!IsEmpty(child)) 
       { 
        copy.Add(child); 
       } 
      } 
      return copy; 
     } 
     return token; 
    } 

    public static bool IsEmpty(JToken token) 
    { 
     return (token.Type == JTokenType.Null); 
    } 
} 

Demo:

string json = @" 
{ 
    ""Foo"": { 
     ""P1"": null, 
     ""P2"": ""hello world"", 
     ""P3"": null, 
     ""P4"": { 
      ""P1"": 1, 
      ""P2"": null, 
      ""P3"": null 
     }, 
     ""FooArray"": [ 
      { 
       ""F1"": null, 
       ""F2"": null, 
       ""F3"": null 
      } 
     ] 
    }, 
    ""Bar"": null 
}"; 

JToken token = JsonHelper.RemoveEmptyChildren(JToken.Parse(json)); 
Console.WriteLine(token.ToString(Formatting.Indented)); 

Ausgang:

{ 
    "Foo": { 
    "P2": "hello world", 
    "P4": { 
     "P1": 1 
    }, 
    "FooArray": [ 
     {} 
    ] 
    } 
} 

Fiddle: https://dotnetfiddle.net/wzEOie

Beachten Sie, dass, nachdem alle Nullwerte Entfernen Sie in der FooArray ein leeres Objekt wird , die Sie nicht wollen. (Und wenn dieses Objekt entfernt würde, dann hätten Sie eine leere FooArray, die Sie auch nicht wollen.) Wenn Sie die Hilfsmethode bei der Entfernung aggressiver machen wollen, können Sie die IsEmpty-Funktion folgendermaßen ändern:

public static bool IsEmpty(JToken token) 
    { 
     return (token.Type == JTokenType.Null) || 
       (token.Type == JTokenType.Array && !token.HasValues) || 
       (token.Type == JTokenType.Object && !token.HasValues); 
    } 

Mit dieser Änderung an Ort und Stelle, würde die Ausgabe wie diese stattdessen aussehen:

{ 
    "Foo": { 
    "P2": "hello world", 
    "P4": { 
     "P1": 1 
    } 
    } 
} 

Fiddle: https://dotnetfiddle.net/ZdYogJ

+0

Danke! Ich habe kurz nach dem Stellen der Frage einen fast ähnlichen Algorithmus gefunden, aber Ihre Antwort ist immer noch gültig :) – PoweredByOrange

+0

Kein Problem; froh, dass ich helfen konnte. –

+0

Ich steckte fest versucht, dies zu erreichen, danke für Ihren Beitrag, es funktioniert! –

2

Brians Antwort funktioniert. Ich habe mir auch kurz nach dem Stellen der Frage eine andere (und immer noch rekursive) Vorgehensweise ausgedacht, falls jemand anderes interessiert ist.

private void RemoveNullNodes(JToken root) 
{ 
    if (root is JValue) 
    { 
     if (((JValue)root).Value == null) 
     { 
      ((JValue)root).Parent.Remove(); 
     } 
    } 
    else if (root is JArray) 
    { 
     ((JArray)root).ToList().ForEach(n => RemoveNullNodes(n)); 
     if (!(((JArray)root)).HasValues) 
     { 
      root.Parent.Remove(); 
     } 
    } 
    else if (root is JProperty) 
    { 
     RemoveNullNodes(((JProperty)root).Value); 
    } 
    else 
    { 
     var children = ((JObject)root).Properties().ToList(); 
     children.ForEach(n => RemoveNullNodes(n)); 

     if (!((JObject)root).HasValues) 
     { 
      if (((JObject)root).Parent is JArray) 
      { 
       ((JArray)root.Parent).Where(x => !x.HasValues).ToList().ForEach(n => n.Remove()); 
      } 
      else 
      { 
       var propertyParent = ((JObject)root).Parent; 
       while (!(propertyParent is JProperty)) 
       { 
        propertyParent = propertyParent.Parent; 
       } 
       propertyParent.Remove(); 
      } 
     } 
    } 
} 
2

Sie können die Null-Token verhindern, erstellt werden beginnen, indem die JsonSerializer mit seinem NullValueHandler Satz NullValueHandler.Ignore angeben. Dies wird als Parameter an JObject.FromObject übergeben, wie in einer Antwort auf die gleiche Frage angezeigt, die Sie verknüpft haben: https://stackoverflow.com/a/29259032/263139.

Verwandte Themen