2015-10-08 5 views
8

J'ai un JObject qui est utilisé comme modèle pour appeler les services Web RESTful. Ce JObject est créé via un analyseur et comme il est utilisé comme un modèle indiquant à l'utilisateur à quoi ressemble le schéma de point de terminaison, j'ai dû trouver un moyen de conserver toutes les propriétés, c'est pourquoi je leur donne null par défaut. Comme à titre d'exemple, voici ce que l'objet ressemble à l'origine comme:JSON.NET sérialiser JObject tout en ignorant les propriétés nulles

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

L'utilisateur est alors en mesure de remplir les champs individuels dont ils ont besoin, comme Foo.P2 et 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 
} 

ce qui signifie qu'ils ne se soucient de ces deux domaines. Maintenant, je veux sérialiser ce modèle (JObject) en une chaîne JSON, mais je veux seulement que les champs qui sont remplis apparaissent. J'ai donc essayé ceci:

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

Malheureusement, cela n'a pas fonctionné. Je suis tombé sur this question et j'ai réalisé qu'une valeur null dans l'objet est un type réel JToken et pas vraiment un null, ce qui est logique. Cependant, dans ce cas très particulier, je dois pouvoir me débarrasser de ces champs "inutilisés". J'ai essayé d'itérer manuellement sur les noeuds et de les supprimer, mais cela n'a pas fonctionné non plus. Notez que le seul type géré que j'utilise est JObject; Je n'ai pas de modèle pour convertir l'objet ou définir des attributs, car ce "modèle" est résolu lors de l'exécution. Je me demandais juste si quelqu'un a rencontré un problème comme celui-ci et a des idées. Toute aide est grandement appréciée!

Répondre

11

Vous pouvez utiliser une méthode d'assistance récursive comme celle ci-dessous pour supprimer les valeurs null de votre hiérarchie JToken avant de la sérialiser.

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); 
    } 
} 

Démo:

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)); 

Sortie:

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

Fiddle: https://dotnetfiddle.net/wzEOie

Notez que, après avoir retiré toutes les valeurs NULL, vous aurez un objet vide dans le FooArray , que vous ne pouvez pas vouloir. (Et si cet objet était supprimé, vous auriez un FooArray vide, que vous ne voudrez peut-être pas non plus.) Si vous voulez rendre la méthode d'assistance plus agressive dans sa suppression, vous pouvez changer la fonction IsEmpty en:

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

Avec ce changement en place, votre sortie pourrait ressembler à ceci:

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

Fiddle: https://dotnetfiddle.net/ZdYogJ

+0

Merci! Je suis venu avec un algorithme presque similaire peu après avoir posté la question mais votre réponse est toujours valide :) – PoweredByOrange

+0

Aucun problème; heureux d'avoir pu aider. –

+0

J'étais coincé en essayant d'y parvenir, merci pour votre contribution, ça marche! –

2

réponse de Brian fonctionne. J'ai également trouvé une autre façon (encore récursive) de le faire peu de temps après avoir posté la question, au cas où quelqu'un d'autre serait intéressé.

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

Vous pouvez empêcher les jetons nuls d'être créé pour commencer en spécifiant le JsonSerializer avec son NullValueHandler ensemble à NullValueHandler.Ignore. Ceci est passé en tant que paramètre à JObject.FromObject comme vu dans une réponse à la même question que vous avez lié à: https://stackoverflow.com/a/29259032/263139.