Converting JSON to C# classes in ASP.Net MVC

2019-04-29 17:15发布

问题:

I'm trying to write an API - the expected output (for a 3rd party) as shown on their website is the JSON below:

{
"api_version" : 4 ,
"hotel_ids" : [97497],   
"hotels" :
    [
        {
            "hotel_id": 97497,
            "room_types":
                {
                    "Fenway Room":
                        {
                            "url": "someurlhere",
                            "price": 178.50, 
                            "room_code": "SINGLE"                               
                        }
                }
        }
    ]
}

I'm using the online tool: http://json2csharp.com/

It gives me the following classes:

public class FenwayRoom
{
public string url { get; set; }
public double price { get; set; }
public string room_code { get; set; }
}

public class RoomTypes
{
public FenwayRoom __invalid_name__Fenway Room { get; set; }
}

public class Hotel
{
public int hotel_id { get; set; }
public RoomTypes room_types { get; set; }
}

public class RootObject
{
public int api_version { get; set; }
public List<int> hotel_ids { get; set; }
public List<Hotel> hotels { get; set; }
}

You can see in RoomTypes:

FenwayRoom __invalid_name__Fenway Room { get; set; }

I'm wondering if their spec for the JSON is wrong, or is there a way for me to create the classes to return the JSON they are expecting?

In the example, I believe the room type "Fenway Room" is a variable - so I don't think it can also be a class name. But it may just be that I don't know how to create a class like this.

I just can't figure out the "Fenway Room" - which I think needs to have something like: "Room Name":"Fenway Room" - but maybe there is another way of defining the classes so that it doesn't need a label "Room Name".

I'd appreciate any help in confirming if it is possible to create classes to match against their JSON.

Thanks, Mark

回答1:

When using Json.NET the output

"room_types":
{
    "Fenway Room":
    {
        "url": "someurlhere",
        "price": 178.50, 
        "room_code": "SINGLE"                               
        }
    }
}

look exactly what you would get serializing a Dictionary.

Change your class to

public class Room
{
    public string url { get; set; }
    public double price { get; set; }
    public string room_code { get; set; }
}

public class Hotel
{
    public int hotel_id { get; set; }
    public Dictionary<string, Room> room_types { get; set; }
}

public class RootObject
{
    public int api_version { get; set; }
    public List<int> hotel_ids { get; set; }
    public List<Hotel> hotels { get; set; }
}

This is all predicated on using Json.NET, which gives you this serialization/deserialization of dictionaries in this format for free. You can do this with the .NET framework serializer, but you need to do extra work.



回答2:

Alter your classes to the following structure:

public class Rootobject
{
    public int api_version { get; set; }
    public List<int> hotel_ids { get; set; }
    public List<Hotel> hotels { get; set; }
}

public class Hotel
{
    public int hotel_id { get; set; }
    public Room_Types room_types { get; set; }
}

public class Room_Types
{
    public List<Room> Rooms { get; set; }
}

public class Room
{
    public string Type { get; set; }
    public string Url { get; set; }
    public float Price { get; set; }
}

Create the following class, which implements the JsonConverter abstract class:

public abstract class MyJsonConverter<T> : JsonConverter
{
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson
        (JsonReader reader, 
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);
        T target = Create(objectType, jObject);
        serializer.Populate(jObject.CreateReader(), target);
        return target;
    }

    public override void WriteJson
        (JsonWriter writer, 
        object value, 
        JsonSerializer serializer)
    {
        //
    }
}

Implement the MyJsonConverter<T> class as follows:

public class RoomConverter : MyJsonConverter<Room_Types>
{
    protected override Room_Types Create(Type objectType, JObject jObject)
    {
        var rooms = jObject.Cast<JToken>().Select(t => new Room
        {
            Type = ((JProperty)t).Name,
            Url = ((JProperty)t).First
                .SelectToken("url").ToString(),
            Price = float.Parse(((JProperty)t).First
                .SelectToken("price").ToString())
        }).ToList();
        return new Room_Types() { Rooms = rooms };
    }
    public override void WriteJson
        (JsonWriter writer, 
        object value, 
        JsonSerializer serializer)
    {
        writer.WriteStartObject();
        ((Room_Types)value).Rooms.ForEach(r =>
        {
            writer.WritePropertyName(r.Type);
            writer.WriteStartObject();
            writer.WritePropertyName("url");
            writer.WriteValue(r.Url);
            writer.WritePropertyName("price");
            writer.WriteValue(r.Price);
            writer.WriteEndObject();
        });
        writer.WriteEndObject();
    }
}

Now you may deserialize / serialize like this:

var result = JsonConvert
    .DeserializeObject<Rootobject>(jsonText, new RoomConverter());
var serialized = JsonConvert
    .SerializeObject(result, new RoomConverter());


回答3:

Try adding the JsonPropteryAttribute.

public class RoomTypes
{
    [JsonProperty(PropertyName="FenWay Room")]
    public FenwayRoom room { get; set; }
}


回答4:

If you use this tool (http://jsonclassgenerator.codeplex.com/) it will generate slightly better C# that supports those property names.

// Generated by Xamasoft JSON Class Generator // http://www.xamasoft.com/json-class-generator

using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Example.SampleResponse1JsonTypes;

namespace Example.SampleResponse1JsonTypes
{

    internal class FenwayRoom
    {

        [JsonProperty("url")]
        public string Url { get; set; }

        [JsonProperty("price")]
        public double Price { get; set; }

        [JsonProperty("room_code")]
        public string RoomCode { get; set; }
    }

    internal class RoomTypes
    {

        [JsonProperty("Fenway Room")]
        public FenwayRoom FenwayRoom { get; set; }
    }

    internal class Hotel
    {

        [JsonProperty("hotel_id")]
        public int HotelId { get; set; }

        [JsonProperty("room_types")]
        public RoomTypes RoomTypes { get; set; }
    }

}

namespace Example
{

    internal class SampleResponse1
    {

        [JsonProperty("api_version")]
        public int ApiVersion { get; set; }

        [JsonProperty("hotel_ids")]
        public int[] HotelIds { get; set; }

        [JsonProperty("hotels")]
        public Hotel[] Hotels { get; set; }
    }

}