简体   繁体   中英

C# best way to deserialize object with count property

Say I am getting this object as a string:

在此处输入图像描述

IN JSON FORM:

{[
  {
    "name": "Monkie Party!!!",
    "venueAdresse": "Bremer strasse 24-;germany",
    "userID": "10",
    "tblEventID": "32",
    "venueCity": "ottersberg",
    "startTimeAndDateUnixTicks": "1629641580",
    "displayStartTime": "1",
    "slots": "500",
    "COUNT(b.status)": "1"
   ]}

And I need to deserialize.

I usually do something like:

public static List<EventType> Deserialize(ApiResponse response)
{
    List<EventType> ad = new List<EventType>();

    var array = (Newtonsoft.Json.Linq.JArray)response.payload;
    foreach (var item in array)
    {
        //using custom converter, to get booleans right
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            Converters = new List<JsonConverter>
             {
                 new BooleanJsonConverter()
             }
        };

        var xy = JsonConvert.DeserializeObject<EventType>(item.ToString());
        ad.Add(xy);

    }
    return ad;
}

But the issue is with the last line of the object (count).

When I have a single count property I usually go:

        string a = array.ToString();
        string b = string.Empty;
        int val = 0;

        for (int i = 0; i < a.Length; i++)
        {
            if (Char.IsDigit(a[i]))
                b += a[i];
        }

        if (b.Length > 0)
            val = int.Parse(b);

But this doesn't work now, as the thumbnail string also contains many digits.

What is the best way to deserialize the number after count and also return the full object?

Your json should look more like this, to be valid (yours seems to be missing } and an attrib name for the array, or it should lack outer { } if it's just a returned array):

{"events": [
  {
    "name": "Monkie Party!!!",
    "venueAdresse": "Bremer strasse 24-;germany",
    "userID": "10",
    "tblEventID": "32",
    "venueCity": "ottersberg",
    "startTimeAndDateUnixTicks": "1629641580",
    "displayStartTime": "1",
    "slots": "500",
    "COUNT(b.status)": "1"
  }
]}

So, we take our json (I took that corrected version above, but you might have to repeat the process if your json is slightly different), and we head over to http://quicktype.io and paste it in there.. It generates this:

// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
//    using SomeNamespace;
//
//    var root = Root.FromJson(jsonString);

namespace SomeNamespace
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class Root
    {
        [JsonProperty("events")]
        public Event[] Events { get; set; }
    }

    public partial class Event
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("venueAdresse")]
        public string VenueAdresse { get; set; }

        [JsonProperty("userID")]
        [JsonConverter(typeof(ParseStringConverter))]
        public long UserId { get; set; }

        [JsonProperty("tblEventID")]
        [JsonConverter(typeof(ParseStringConverter))]
        public long TblEventId { get; set; }

        [JsonProperty("venueCity")]
        public string VenueCity { get; set; }

        [JsonProperty("startTimeAndDateUnixTicks")]
        [JsonConverter(typeof(ParseStringConverter))]
        public long StartTimeAndDateUnixTicks { get; set; }

        [JsonProperty("displayStartTime")]
        [JsonConverter(typeof(ParseStringConverter))]
        public long DisplayStartTime { get; set; }

        [JsonProperty("slots")]
        [JsonConverter(typeof(ParseStringConverter))]
        public long Slots { get; set; }

        [JsonProperty("COUNT(b.status)")]
        [JsonConverter(typeof(ParseStringConverter))]
        public long CountBStatus { get; set; }
    }

    public partial class Root
    {
        public static Root FromJson(string json) => JsonConvert.DeserializeObject<Root>(json, SomeNamespace.Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this Root self) => JsonConvert.SerializeObject(self, SomeNamespace.Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters =
            {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }

    internal class ParseStringConverter : JsonConverter
    {
        public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?);

        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            var value = serializer.Deserialize<string>(reader);
            long l;
            if (Int64.TryParse(value, out l))
            {
                return l;
            }
            throw new Exception("Cannot unmarshal type long");
        }

        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
        {
            if (untypedValue == null)
            {
                serializer.Serialize(writer, null);
                return;
            }
            var value = (long)untypedValue;
            serializer.Serialize(writer, value.ToString());
            return;
        }

        public static readonly ParseStringConverter Singleton = new ParseStringConverter();
    }
}

And you use it like as described in the comment at the top - get the response string, and call

var root = Root.FromJson(jsonString);

You're after an event list, so that's in root.Events


The crucial bit I suppose for your case is the JsonProperty parts, but the rest of it makes for a much nicer experience...

Don't forget, if your API has a swagger/openapi endpoint that you can use something like AutoRest, NSwag etc to do even more of this for you - those apps will write a full http client system, deserialize etc so all you really need to do is like:

var c = new SomeClient("http://myapi.com");

var events = c.GetEvents();

You download the swagger json, you feed it into NSwag, it writes a few thousand lines of code that nicely sets up reused httpclients etc to do all the call, response management etc.. If the swagger says the api calls throw error codes, then they get worked in too. The overall aim is that your API creator (you?) just writes code, and maybe some comments, of the COntroller methods - GetEvents, PutEvent etc.. Then all that code is read by eg Swashbuckle, and turned into a swagger.json file that describes every part of the API, the enums, the return types etc in great detail.. then you run another program at the other end to consume that json and generate more code that calls the API, and a suite of objects to feed into it.. So really all you have to do is write an API, and then write code that uses the autogenerated cleint (that was created from the API definition)

It's the reiteration of an older tech (that was great, imho, because it worked really well) of web services and WSDL where you just wrote your webservice, publishing it and turning on WSDL generation meant you could then go to VS and choose "add web service reference" - VS consumed the WSDL and churned out a working client object that you called methods on and it invoked the api and did all the parsing etc.. It's taken years to catch back up to that state, but we're getting there

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM