Удалить пустые свойства объекта, отправленного Json MVC

namespace Booking.Areas.Golfy.Models
{
    public class Time
    {
        public string   time            { get; set; }
        public int      holes           { get; set; }
        public int      slots_available { get; set; }
        public decimal? price           { get; set; }
        public int?     Nextcourseid    { get; set; }

        public bool ShouldSerializeNextcourseid
        {
            get
            {
                return this.Nextcourseid != null;
            }
        }

        public bool? allow_extra { get; set; }

        public bool ShouldSerializeallow_extra
        {
            get
            {
                return this.allow_extra != null;
            }
        }
    }
}


namespace Booking.Areas.Golfy.Controllers
{
    public class TeetimesController : Controller
    {
        //
        // GET: /Golfy/Teetimes/
        public ActionResult Index(
            string date,
            int?   courseid = null,
            int?   clubid = null,
            int?   holes = null,
            int?   available = null,
            int?   prices = null
        )
        {
            var DateFrom = DateTime.ParseExact(date, "yyyy-MM-dd", CultureInfo.InvariantCulture);

            Teetimes r = BookingManager.GetBookings(new Code.Classes.Params.ParamGetBooking()
            {
                ClubID = clubid
                , DateFrom = DateFrom
                , DateTo = DateFrom.AddDays(1)
                , GroundID = courseid
            });

            return Json(r, JsonRequestBehavior.AllowGet);
        }
    }
}

Webservice выше возвращает строку json с несколькими значениями времени.

Я хотел бы, чтобы свойства Nextcourseid и allow_extra были исключены из вывода, когда их значения равны нулю.

Я попробовал ShouldSerializeXxx, но он не работает.
FYI: Я также пробовал [ScriptIgnore], который работает, но не является условным.

Ответ 1

Вы не можете использовать значение по умолчанию Json ActionResult для удаления нулевых свойств.

Вы можете посмотреть JSON.NET, у него есть атрибут, который вы можете установить для удаления свойства, если оно равно null

[JsonProperty(NullValueHandling=NullValueHandling.Ignore)]

Или, если вы не хотите использовать другие библиотеки, вы можете создать свой собственный пользовательский ActionResult json и зарегистрировать новый конвертер по умолчанию JavaScriptSerializer, например:

public class JsonWithoutNullPropertiesResult : ActionResult
{
    private object Data { get; set; }

    public JsonWithoutNullPropertiesResult(object data)
    {
        Data = data;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = "application/x-javascript";
        response.ContentEncoding = Encoding.UTF8;

        if (Data != null)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.RegisterConverters(new[] { new NullPropertiesConverter() });
            string ser = serializer.Serialize(Data);
            response.Write(ser);
        }
    }
}

public class NullPropertiesConverter : JavaScriptConverter
{
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        var toSerialize = new Dictionary<string, object>();

        foreach (var prop in obj.GetType()
                                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                .Select(p => new
                                {
                                    Name = p.Name,
                                    Value = p.GetValue(obj)
                                })
                                .Where(p => p.Value != null))
        {
            toSerialize.Add(prop.Name, prop.Value);
        }

        return toSerialize;
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get { return GetType().Assembly.GetTypes(); }
    }
}

И теперь, на ваш взгляд:

public ActionResult Index()
{
    Teetimes r = BookingManager.GetBookings();
    return new JsonWithoutNullPropertiesResult(t);
}

Ответ 2

У меня всегда были проблемы с встроенным json-сериализатором, поэтому я использую Json.NET. Здесь небольшой пример тестирования этих двух сериализаторов:

public class Model {
    public int Id { get; set; }
    public string Name { get; set; }

    public bool ShouldSerializeName() {
        return Name != null;
    }
}

class Program {
    static void Main(string[] args) {
        var t1 = new Model {
            Name = "apw8u3rdmapw3urdm",
            Id = 298384
        };
        var t2 = new Model {
            Id = 234235
        };

        Test(t1);
        Test(t2);
    }

    static void Test(Model model) {
        Console.WriteLine("JSon from .Net: {0}", ToJson(model));
        Console.WriteLine("JSon from JSon.Net: {0}", ToDotNetJson(model));
    }

    static string ToJson(Model model) {
        var s = new JavaScriptSerializer();
        return s.Serialize(model);
    }

    static string ToDotNetJson(Model model) {
        return JsonConvert.SerializeObject(model);
    }
}

Вы должны включить System.Web.Extensions в качестве зависимости и установить Json.Net с помощью nuget, чтобы этот пример работал.

Вот некоторая форма документации Json.NET и встроенный в сериализатор Framework

Ответ 3

Полученные ответы являются interresting, но я начал использовать DataContractJsonSerializer тем временем.
И он отлично справляется с работой без необходимости использования сторонней структуры (хотя JSON.Net действительно кажется широко используемым).

public ActionResult Index(
    string date
    , int? courseid = null
    , int? clubid = null
    , int? holes = null
    , int? available = null
    , int? prices = null
)
{
    var DateFrom = DateTime.ParseExact(date, "yyyy-MM-dd", CultureInfo.InvariantCulture);

    MTeetimes r = BookingManager.GetBookings(new Code.Classes.Params.ParamGetBooking()
    {
        ClubID = clubid
        , DateFrom = DateFrom
        , DateTo = DateFrom.AddDays(1)
        , GroundID = courseid
    });

    // return Json(r, JsonRequestBehavior.AllowGet);

    string response;
    var serializer = new DataContractJsonSerializer(typeof(MTeetimes));

    // Serialize
    using (var ms = new MemoryStream())
    {
        serializer.WriteObject(ms, r);
        response = Encoding.Default.GetString(ms.ToArray());
    }

    return Content(response);
}


[DataContract]
public class Time
{
    [DataMember(Name="time", EmitDefaultValue = false)]
    public string Time
    {
        get;
        set;
    }

    [DataMember(Name = "holes", EmitDefaultValue = false)]
    public int Holes
    {
        get;
        set;
    }

    [DataMember(Name = "slots_available", EmitDefaultValue = false)]
    public int Slots_available
    {
        get;
        set;
    }

    [DataMember(Name = "price", EmitDefaultValue = false)]
    public decimal? Price
    {
        get;
        set;
    }

    [DataMember(Name = "nextcourseid", EmitDefaultValue = false)]
    public int? Nextcourseid
    {
        get;
        set;
    }

    [DataMember(Name = "allow_extra", EmitDefaultValue = false)]
    public bool? Allow_extra
    {
        get;
        set;
    }
}