Как пропустить / игнорировать / пропускать пустые литералы объектов в выпуске JSON?

Я использую Json.NET для преобразования сложного графа объектов C# в JSON. Из-за игнорирования свойств, которые имеют значения по умолчанию в объекте, я обычно получаю пустые литералы объектов на выходе, которые я бы хотел опустить.

Например:

 public class Sample { public int Value { get; set; } public string Name { get; set; } } public class ParentSample { // this property should never be null, hence the initializer public Sample Sample { get; } = new Sample(); } .. var obj = new ParentSample(); // settings for indentation and excluding default values omitted for clarity var output = JsonConvert.SerializeObject(obj, ... ); // output will be // { // Sample: {} // } // // I'd like it to be // {} 

Я знаю некоторые конкретные решения, такие как добавление булевского метода ShouldSerializeSample к типу ParentSample и проверка наличия по умолчанию всех свойств. Однако я хотел бы получить общее решение в виде пользовательского адаптера контракта, например.

В комментариях похоже, что вы решили прибегнуть к использованию Regex, чтобы избавиться от пустых объектов. Одна из проблем с этой идеей заключается в том, что она, вероятно, не будет обрабатывать ситуацию, когда у вас есть то, что я назову «рекурсивные пустые объекты». Другими словами, что-то вроде этого:

 { "foo": { "bar": {}, "baz": {} } } 

Если вам удастся выровнять bar с пустым объектом с самым высоким уровнем и baz с помощью Regex (хотя вы также понимаете, что вам нужно удалить запятую между ними, чтобы JSON был действителен), вы все равно оставите пустой объект: foo .

 { "foo": { } } 

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

 using System; using Newtonsoft.Json.Linq; public static class JsonHelper { public static string SerializeToMinimalJson(object obj) { return JToken.FromObject(obj).RemoveEmptyChildren().ToString(); } public static JToken RemoveEmptyChildren(this JToken token) { if (token.Type == JTokenType.Object) { JObject copy = new JObject(); foreach (JProperty prop in token.Children()) { JToken child = prop.Value; if (child.HasValues) { child = child.RemoveEmptyChildren(); } if (!child.IsEmptyOrDefault()) { 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 = child.RemoveEmptyChildren(); } if (!child.IsEmptyOrDefault()) { copy.Add(child); } } return copy; } return token; } public static bool IsEmptyOrDefault(this JToken token) { return (token.Type == JTokenType.Array && !token.HasValues) || (token.Type == JTokenType.Object && !token.HasValues) || (token.Type == JTokenType.String && token.ToString() == String.Empty) || (token.Type == JTokenType.Boolean && token.Value() == false) || (token.Type == JTokenType.Integer && token.Value() == 0) || (token.Type == JTokenType.Float && token.Value() == 0.0) || (token.Type == JTokenType.Null); } } 

Затем вы можете сериализовать свой объект (ы) следующим образом:

 var json = JsonHelper.SerializeToMinimalJson(obj); 

Сценарий: https://dotnetfiddle.net/awRPMR

РЕДАКТИРОВАТЬ

Если вы хотите почитать [DefaultValue] этим методом, вы можете сделать это, изменив метод SerializeToMinimalJson() чтобы создать экземпляр JsonSerializer , установив на DefaultValueHandling свойство DefaultValueHandling , а затем передав его в JToken.FromObject() как показано ниже. (Это нужно сделать так, потому что у JTokens нет ссылок на исходные объекты, из которых они были созданы с помощью FromObject() , поэтому после этого невозможно получить значения атрибутов [DefaultValue] .)

 public static string SerializeToMinimalJson(object obj) { var serializer = new JsonSerializer(); serializer.NullValueHandling = NullValueHandling.Ignore; serializer.DefaultValueHandling = DefaultValueHandling.Ignore; return JToken.FromObject(obj, serializer).RemoveEmptyChildren().ToString(); } 

Если вы это сделаете, вы также можете изменить метод IsEmptyOrDefault() чтобы он не IsEmptyOrDefault() значения, которые являются стандартными по умолчанию. Вы можете уменьшить его до:

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

Сценарий: https://dotnetfiddle.net/0yVRI5

Вы можете дать JsonSerializerSettings для метода, используя NullValueHandling.Ignore :

 var output = JsonConvert.SerializeObject(obj, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); 

Если эти настройки не дают то, что вам нужно, проверьте: документацию. Там вы найдете все свойства и описание.

Изменить: использование дочернего элемента (Sample) в качестве структуры работает с DefaultValueHandling.Ignore. Но @ Zoltán Tamási будет использовать регулярное выражение из-за сложности classа.

Я внедрил немного другое решение, которое использует общий метод, reflection и некоторую стандартную функциональность Newtonsoft.Json ShouldSerialize. Не элегантный, но концептуально простой для моей конкретной потребности. Ниже приведен fragment кода LinqPad.

 void Main() { Person person = new Person(); person.MyAddress = new Address(); var ret = person.ShouldSerializeMyAddress(); var json = JsonConvert.SerializeObject(person, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); json.Dump(); } public static class JsonExtensions { public static bool ShouldSerialize(this object self) { if (self == null) return false; var methods = self.GetType().GetMethods().Where(p => p.Name.StartsWith("ShouldSerialize")); return methods.Any(p => p.Invoke(self, null) is bool value && value); } } public class Person { public Address MyAddress { get; set; } public bool ShouldSerializeMyAddress() { return MyAddress.ShouldSerialize(); } } public class Address { public string Street { get; set; } public bool ShouldSerializeStreet() { return false; // or whatever your property serialization criteria should be } public string City { get; set; } public bool ShouldSerializeCity() { return false; } public string State { get; set; } public bool ShouldSerializeState() { return false; } public string Zip { get; set; } public bool ShouldSerializeZip() { return false; } } 
  • Сериализовать словарь как массив (пар ключей)
  • Дезициализация свойств самореференции не работает
  • .NET NewtonSoft JSON десериализует карту для другого имени свойства
  • Заставить JSON.NET включать миллисекунды при сериализации DateTime (даже если компонент ms равен нулю)
  • Разбор большого json-файла в .NET.
  • Как я могу вернуть CamelCase JSON, сериализованный JSON.NET из методов controllerа ASP.NET MVC?
  • Серийный объект Json.NET с корневым именем
  • Как игнорировать неизвестные значения enum во время десериализации json?
  • Как получить json.net для сериализации членов classа, полученных из List ?
  • Как использовать JSON.NET для десериализации в вложенный / рекурсивный словарь и список?
  • Как десериализовать данные JSON?
  • Давайте будем гением компьютера.