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
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.
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());
Try adding the JsonPropteryAttribute.
public class RoomTypes
{
[JsonProperty(PropertyName="FenWay Room")]
public FenwayRoom room { get; set; }
}
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; }
}
}