Deserialize complex json into an object - c#

I have a strange json, which:
I can not change it, it is from a third party
It can have up to 75 properties
A simple example of the json:
{
"bookingcode":["ABC","DEF", "GHJ", "TEST"],
"referencenumber":[123, 456]
"bookingdate":["22-07-2022T14:00:30", "23-11-2022T17:00:25"]
}
I am only interested in the last value of the array and want to deserialize into a class called Booking, so in this case bookingcode="TEST" and bookingdate="23-11-2022T17:00:25".
A solution would be define a model:
public class Booking
{
public string BookingCode { get; set; }
public int ReferenceNumber { get; set; }
public DateTime BookingDate { get; set;}
}
public class BookingInput
{
public List<string> BookingCode { get; set;}
public List<int> ReferenceNumber { get; set; }
public List<DateTime> BookingDate { get; set; }
}
var bookinginput = JsonSerializer.Deserialize<BookingInput>(jsonString);
var booking = new Booking
{
BookingCode = bookinginput.BookingCode.Last(),
ReferenceNumber = bookinginput.ReferenceNumber.Last(),
...
}
I think it is very tedious to write out all 75 properties in code like this. Is there a better solution in Json.net or System.Text.Json for this?

Using NewtonSoft.Json, you can create your own JsonConverter. Below I have created a LastArrayItemJsonConverter, which takes the last item from an array of a certain type and returns that.
public class LastArrayItemJsonConverter<TItem> : JsonConverter<TItem>
{
private string _formatString;
public LastArrayItemJsonConverter()
{ }
public LastArrayItemJsonConverter(string formatString)
{
_formatString = formatString;
}
public override TItem ReadJson(JsonReader reader, Type objectType, TItem existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (typeof(TItem) == typeof(DateTime) || typeof(TItem) == typeof(DateTime?))
reader.DateFormatString = _formatString;
TItem result = default;
if (reader.TokenType != JsonToken.StartArray)
return default;
while (reader.Read() && reader.TokenType != JsonToken.EndArray)
result = (TItem)Convert.ChangeType(reader.Value, typeof(TItem));
return result;
}
public override void WriteJson(JsonWriter writer, TItem value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
By decorating your model, you can specify that the serializer should use the converter to convert the properties:
public class Booking
{
[JsonConverter(typeof(LastArrayItemJsonConverter<string>))]
public string BookingCode { get; set; }
[JsonConverter(typeof(LastArrayItemJsonConverter<int>))]
public int ReferenceNumber { get; set; }
[JsonConverter(typeof(LastArrayItemJsonConverter<DateTime>), "dd-MM-yyyy\\THH:mm:ss")]
public DateTime BookingDate { get; set; }
}
Now, the model's properties will be populated with the last values from the arrays. Deserialize the json using:
var booking = JsonConvert.DeserializeObject<Booking>(json)

You can define a single Booking class which includes
the BookingInput's properties
and the last item retrieval logics as well
public class Booking
{
[JsonIgnore]
public string BookingCode => BookingCodes.Last();
[JsonIgnore]
public int ReferenceNumber => ReferenceNumbers.Last();
[JsonIgnore]
public DateTime BookingDate => BookingDates.Last();
[JsonProperty("bookingcode")]
public List<string> BookingCodes { get; set; } = new List<string>();
[JsonProperty("referencenumber")]
public List<int> ReferenceNumbers { get; set; } = new List<int>();
[JsonProperty("bookingdate")]
public List<DateTime> BookingDates { get; set; } = new List<DateTime>();
}

if you want to use your custom c# class
using Newtonsoft.Json;
var jsonParsed = JObject.Parse(json);
foreach (var arr in jsonParsed.Properties())
jsonParsed[arr.Name] = arr.Value.LastOrDefault();
Booking booking = jsonParsed.ToObject<Booking>();
but since you can have up to 75 properties, maybe it would be better to use a dictionary instead of a custom class
Dictionary<string, object> bookingDict = new Dictionary<string, object>();
foreach (var arr in jsonParsed.Properties())
bookingDict.Add(arr.Name, arr.Value.LastOrDefault());
result
{
"bookingcode": "TEST",
"referencenumber": 456,
"bookingdate": "23-11-2022T17:00:25"
}

It's better if you provide your request code too. did you add ReadAsStringAsync(); after the request?
public class Booking
{
[JsonProperty("bookingcode")]
public List<string> BookingCodes { get; set; } = new List<string>();
[JsonProperty("referencenumber")]
public List<int> ReferenceNumbers { get; set; } = new List<int>();
[JsonProperty("bookingdate")]
public List<DateTime> BookingDates { get; set; } = new List<DateTime>();
}
And some web request sample
using (var client = new HttpClient())
{
var response = await client.
GetAsync(apiEndpoint);
var responseText = await response.Content.ReadAsStringAsync();
var compareResult = JsonSerializer.Deserialize<Booking>(responseText)??new Booking();
return compareResult;
}

Related

Deserialize property to different class based on other property c#

I know there are many similar questions on SO, however all the ones I've found require a shared base class in order to work.
With a stream of JSON data like this:
[
{
"webhookType": "order",
"data": {
"id": "eeiefj393",
"orderProperty": "Value"
}
},
{
"webhookType": "customer",
"data": {
"id": 29238,
"customerProperty": "Value"
}
}
]
I wish to deserialize this into two containers, List<Customer> and List<Order>. Where the two classes are as follows:
class Order
{
public string Id { get; set; }
public string OrderProperty { get; set; }
[...]
}
class Customer
{
public long Id { get; set; }
public string CustomerProperty { get; set; }
[...]
}
There may be shared property names however there are no shared properties + type between these two classes and so the solutions involing a sub class aren't working for me.
You need to create a JsonConverter.
DataConverter
public class WebHookConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject item = JObject.Load(reader);
if (item["webhookType"].Value<string>() == "order")
{
var webhook = new WebHook
{
Type = item["webhookType"].Value<string>(),
Data = item["data"].ToObject<Order>()
};
return webhook;
}
else if (item["webhookType"].Value<string>() == "customer")
{
var webhook = new WebHook
{
Type = item["webhookType"].Value<string>(),
Data = item["data"].ToObject<Customer>()
};
return webhook;
}
}
return null;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
Objects
[JsonConverter(typeof(WebHookConverter))]
public class WebHook
{
[JsonProperty("webhookType")]
public string Type { get; set; }
public object Data { get; set; }
}
public class Order
{
public string Id { get; set; }
[JsonProperty("orderProperty")]
public string Property { get; set; }
}
public class Customer
{
public long Id { get; set; }
[JsonProperty("customerProperty")]
public string Property { get; set; }
}
Serialization
var json = File.ReadAllText("json1.json");
var obj = JsonConvert.DeserializeObject<List<WebHook>>(json);
var orderList = obj.Where(o => o.Type == "order").Select(o => o.Data).ToList();
var customerList = obj.Where(o => o.Type == "customer").Select(o => o.Data).ToList();
Output:

Deserialize a json string when there is no field name.in c#

Normally when I use API's and get a Json string back I simply make a class to suit the string and populate that class using newton JsonConvert.DeserializeObject.
However I now have a Json string which 1 of the fields does not have a name.
{
"attacks": {
"114862720": {
"code": "115dc2b990153c41c33d519b26cc302a",
"timestamp_started": 1596782220,
"timestamp_ended": 1596782226,
"attacker_id": 580816,
"attacker_name": "chedders",
"attacker_faction": 32585,
"attacker_factionname": "Heart of a Pirate",
"defender_id": 65306,
"defender_name": "-Clansdancer",
"defender_faction": 0,
"defender_factionname": null,
"result": "Attacked",
"stealthed": 0,
"respect_gain": 4.14,
"chain": 3,
"modifiers": {
"fairFight": 3,
"war": 1,
"retaliation": 1,
"groupAttack": 1,
"overseas": 1,
"chainBonus": 1
}
},
"114862829": {
"code": "8bf08c8ceb9b72f05f40235310cd822e",
"timestamp_started": 1596782339,
"timestamp_ended": 1596782344,
"attacker_id": 580816,
"attacker_name": "chedders",
"attacker_faction": 32585,
"attacker_factionname": "Heart of a Pirate",
"defender_id": 964979,
"defender_name": "brko21",
"defender_faction": 0,
"defender_factionname": null,
"result": "Attacked",
"stealthed": 0,
"respect_gain": 4.11,
"chain": 4,
"modifiers": {
"fairFight": 3,
"war": 1,
"retaliation": 1,
"groupAttack": 1,
"overseas": 1,
"chainBonus": 1
}
}
}
}
After attacks is an ID which is unique to each entry.so building a class for this as I normally would just wont work as the ID is unknown.
Any pointers on how to deserialise this string would be most welcome.
You can use the [JsonExtensionData] property.
Here's the official example:
public class DirectoryAccount
{
// normal deserialization
public string DisplayName { get; set; }
// these properties are set in OnDeserialized
public string UserName { get; set; }
public string Domain { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
// SAMAccountName is not deserialized to any property
// and so it is added to the extension data dictionary
string samAccountName = (string)_additionalData["SAMAccountName"];
Domain = samAccountName.Split('\\')[0];
UserName = samAccountName.Split('\\')[1];
}
public DirectoryAccount()
{
_additionalData = new Dictionary<string, JToken>();
}
}
So in your OnDeserializing method, you can get the values from the dictionary and add them to a proper field/cast them to a list of objects, etc.
This would deserialise to a Dictionary<string, Attack>. Where Attack is a type that you define with all of the properties within each object, in the JSON document.
This example assumes you're using NewtonSofts JSON library:
var attacks = JsonConvert.DeserializeObject<Dictionary<string, Attack>>(jsonString);
public class Attack {
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("timestamp_started")]
public long TimestampStarted { get; set; }
[JsonProperty("timestamp_ended")]
public long TimestampEnded { get; set; }
[JsonProperty("attacker_id")]
public int AttackerId { get; set; }
[JsonProperty("attacker_name")]
public string AttackerName { get; set; }
[JsonProperty("attacker_faction")]
public int AttackerFaction { get; set; }
[JsonProperty("attacker_factionname")]
public string AttackerFactionName { get; set; }
[JsonProperty("defender_id")]
public int DefenderId { get; set; }
[JsonProperty("defender_name")]
public string DefenderName { get; set; }
[JsonProperty("defender_faction")]
public int DefenderFaction { get; set; }
[JsonProperty("defender_factionname")]
public string DefenderFactionName { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("stealthed")]
public int Stealthed { get; set; }
[JsonProperty("respect_gain")]
public decimal RespectGain { get; set; }
[JsonProperty("chain")]
public int Chain { get; set; }
[JsonProperty("modifiers")]
public Dictionary<string, int> Modifiers { get; set; }
}
This results in a collection of identifiers against a strongly typed field set.
var allAttacksByFaction = attacks.Where(x => x.Value.AttackerFaction == 1234);
var singleAttack = attacks.Single(x => x.Key == "<value of attack identifier>");
Try this json converter.
class AttacksConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Attacks[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject attacks = serializer.Deserialize<JObject>(reader);
Dictionary<string, AttackDetails> result = new Dictionary<string, AttackDetails>();
foreach (JProperty property in attacks.Properties())
{
string attackKey = property.Name;
Attacks attackValue = property.Value.ToObject<Attacks>();
result.Add(attackKey, new AttackDetails()
{
Code = attackValue.Code
});
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
The full code can be found here: https://github.com/tmutton/StackOverflowQuestion63544325

How do I re-architect my model types to reduce code replication

I have 3 model types:
public class BankA_Transaction : BanKTransactionMetaData
{
public string GROUPName { get; set; }
public string ACC_ID { get; set; }
public string ACCOUNT_NO { get; set; }
}
public class BankB_Transaction : BanKTransactionMetaData
{
public string Name { get; set; }
public string ACC_ID { get; set; }
public string ACCOUNT_NO { get; set; }
}
public class BankC_Transaction : BanKTransactionMetaData
{
public string FullName { get; set; }
public string ACC_ID { get; set; }
public string ACCOUNT_NO { get; set; }
}
Note: The actual property lists are much longer
All of which inherit some fields needed when saving into the database.
public class BanKTransactionMetaData
{
public String BankName { get; set; }
}
These models get filled with records from a file sent by the bank and then saved to a database.
As part of this save I convert the records to JSON as that is required by the database.
public void SaveBankA(BankA bankA)
{
bankA.BankName = "Bank_A";
string jsonText = JsonConvert.SerializeObject(bankA_Transaction, Formatting.Indented);
Code for saving...
At the moment I have a different methods for SaveBankA, SaveBankA and SaveBankB.
It seems to me that this is code replication and that I should get all the models to inherit better in order to use a base type? instead of each named type.
I've read up on Abstract and Virtual classes as I suspect that's what I need but I can't work out how to plug it together.
I can't just use Object in SaveBankA as I need to add .BankName.
Is there a better architecture to reduce code replication?
Perhaps you need something like this?
In base service class:
protected void SaveBankTransaction(BankTransactionMetaData tran)
{
string jsonText = JsonConvert.SerializeObject(tran, Formatting.Indented);
// additional saving code
}
In child service classes:
public void SaveBankA(BankA bankA)
{
bankA.BankName = "Bank_A";
base.SaveBankTransaction(bankA);
}
Create a couple of interfaces, one for your meta data (IBankData) and one for your bank transaction details (IBankTransaction). The IBankData interface will maintain a reference to the IBankTransaction interface. This should also allow you to add additional banks when needed, e.g. Bank D.
public interface IBankData
{
string BankName { get; }
// ... additional bank meta data properties
// ...
IBankTransaction Transaction { get; set; }
}
public interface IBankTransaction
{
[JsonProperty("ACC_ID")]
string AccountId { get; set; }
[JsonProperty("ACCOUNT_NO")]
string AccountNumber { get; set; }
// ... additional shared bank transaction properties
// ...
}
FYI, I chose to use the JsonProperty attribute to control the name for the JSON key, this allows the class properties to be named according to best practices without affecting the JSON property names.
Next implement the interfaces for each bank you will be working with. In each bank add the additional properties that will only apply to each implementation, i.e. since the GroupName property is only used by BankA, this property will be added to the BankA class and not the interface. The same goes for any other bank specific properties.
Bank A
public class BankA : IBankData
{
public string BankName => "BankA";
public IBankTransaction Transaction { get; set; }
}
public class BankATransaction : IBankTransaction
{
// Bank A specific properties
[JsonProperty("GROUPName")]
public string GroupName { get; set; }
// ... additional Bank A specific properties
// ...
// interface implemented properties
public string AccountId { get; set; }
public string AccountNumber { get; set; }
}
Bank B
public class BankB : IBankData
{
public string BankName => "BankB";
public IBankTransaction Transaction { get; set; }
}
public class BankBTransaction : IBankTransaction
{
// Bank B specific properties
public string Name { get; set; }
// ... additional Bank B specific properties
// ...
// interface implemented properties
public string AccountId { get; set; }
public string AccountNumber { get; set; }
}
Bank C
public class BankC : IBankData
{
public string BankName => "BankC";
public IBankTransaction Transaction { get; set; }
}
public class BankCTransaction : IBankTransaction
{
// Bank B specific properties
public string FullName { get; set; }
// ... additional Bank B specific properties
// ...
// interface implemented properties
public string AccountId { get; set; }
public string AccountNumber { get; set; }
}
JsonConverter
Since the IBankTransaction is a property within the IBankData this will change your JSON structer. You may not want this, to retain your structure, a JsonConverter can be implemented on the IBankData interface. This will remove the Transaction object in the JSON and move the child properties under the JSON root.
public class BankJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
JProperty transactionProperty = o.Properties().FirstOrDefault(p => p.Name == "Transaction");
o.Remove("Transaction");
JToken token = transactionProperty;
foreach (JToken ct in token.Children())
{
foreach (var prop in JProperty.FromObject(ct))
{
o.Add(prop);
}
}
serializer.Serialize(writer, o);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead => false;
public override bool CanConvert(Type objectType)
{
return objectType.GetInterfaces().Contains(typeof(IBankData));
}
}
Usage
For the usage example I've created a few test functions to prep the data and added SaveBank method that you can relocate in your actual code as it would make sense for your solution.
class Program
{
static void Main(string[] args)
{
string bankATransJson = GetBankATestJsonInput();
BankATransaction bankATransaction = JsonConvert.DeserializeObject<BankATransaction>(bankATransJson);
BankA bankA = new BankA();
bankA.Transaction = bankATransaction;
Console.WriteLine(SaveBank(bankA));
// output:
// {
// "BankName": "BankA",
// "GROUPName": "g54321",
// "ACC_ID": "A01",
// "ACCOUNT_NO": "A1111"
// }
string bankBInputJson = GetBankBTestJsonInput();
BankBTransaction bankBTransInput = JsonConvert.DeserializeObject<BankBTransaction>(bankBInputJson);
BankB bankB = new BankB();
bankB.Transaction = bankBTransInput;
Console.WriteLine(SaveBank(bankB));
// output:
// {
// "BankName": "BankB",
// "ACC_ID": "B02",
// "ACCOUNT_NO": "B2222",
// "Name": "Bank_Of_B
// }
string bankCInputJson = GetBankCTestJsonInput();
BankCTransaction bankCTransInput = JsonConvert.DeserializeObject<BankCTransaction>(bankCInputJson);
BankC bankC = new BankC();
bankC.Transaction = bankCTransInput;
Console.WriteLine(SaveBank(bankC));
// output:
// {
// "BankName": "BankC",
// "ACC_ID": "C03",
// "ACCOUNT_NO": "C3333",
// "FullName": "C Bank"
// }
}
public static string SaveBank(IBankData bankData)
{
// when calling the serialize object method, we pass our BankJsonConverter
string jsonText = JsonConvert.SerializeObject(bankData, Formatting.Indented, new BankJsonConverter());
// this example just returns the JSON text
// but you would implement your save logic as needed
return jsonText;
}
private static string GetBankATestJsonInput()
{
var obj = new { ACC_ID = "A01", ACCOUNT_NO = "A1111", GROUPName = "g54321" };
return JsonConvert.SerializeObject(obj);
}
private static string GetBankBTestJsonInput()
{
var obj = new { ACC_ID = "B02", ACCOUNT_NO = "B2222", Name = "Bank_Of_B" };
return JsonConvert.SerializeObject(obj);
}
private static string GetBankCTestJsonInput()
{
var obj = new { ACC_ID = "C03", ACCOUNT_NO = "C3333", FullName = "C Bank" };
return JsonConvert.SerializeObject(obj);
}
}

Deserializing JSON search results into strongly typed lists (JSON.NET)

I have search results in JSON and I want to deserialize it into strongly typed objects
For example:
{
searchresult: {
resultscount: 15,
results: [
{
resultnumber: 1,
values: [
{
key: "bookid",
value: 1424
},
{
key: "name",
value: "C# in depth"
},
]
}
]
}
}
And I have this POCO
public class Book {
public int BookId { get; set; }
public string Name { get; set; }
}
I want to get a list of books. Yes, I can write my own custom deserializer for this case, but I want to use a default deserializer.
Is it possible to do something like that?
IEnumerable<Book> books = JsonConvert.DeserializeObject<IEnumerable<Book>>(json);
Json.NET will not do this without any customization. You have the following approaches:
Post your classes to http://json2csharp.com/ to create a literal representation of your JSON, then use Linq to transform the result into a list of Book classes:
public class Value
{
public string key { get; set; }
public string value { get; set; } // Type changed from "object" to "string".
}
public class Result
{
public int resultnumber { get; set; }
public List<Value> values { get; set; }
}
public class Searchresult
{
public int resultscount { get; set; }
public List<Result> results { get; set; }
}
public class RootObject
{
public Searchresult searchresult { get; set; }
}
And then
var root = JsonConvert.DeserializeObject<RootObject>(json);
var books = root.searchresult.results.Select(result => new Book { Name = result.values.Find(v => v.key == "name").value, BookId = result.values.Find(v => v.key == "bookid").value });
Create a custom JsonConverter to convert the JSON to your POCO as it is being read, for instance:
internal class BookConverter : JsonConverter
{
public override bool CanWrite
{
get
{
return false;
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Book);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var values = serializer.Deserialize<List<KeyValuePair<string, string>>>(reader);
if (values == null)
return existingValue;
var book = existingValue as Book;
if (book == null)
book = new Book();
// The following throws an exception on missing keys. You could handle this differently if you prefer.
book.BookId = values.Find(v => v.Key == "bookid").Value;
book.Name = values.Find(v => v.Key == "name").Value;
return book;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class Result
{
public int resultnumber { get; set; }
[JsonProperty("values")]
[JsonConverter(typeof(BookConverter))]
public Book Book { get; set; }
}
public class Searchresult
{
public int resultscount { get; set; }
public List<Result> results { get; set; }
}
public class RootObject
{
public Searchresult searchresult { get; set; }
}
and then
var root = JsonConvert.DeserializeObject<RootObject>(json);
var books = root.searchresult.results.Select(result => result.Book);
Here I only implemented ReadJson as your question only asks about deserialization . You could implement WriteJson similarly if required.
Use Linq to JSON to load the JSON into a structured hierarchy of JObject's then convert the result to Book's with Linq:
var books =
JObject.Parse(json).Descendants()
.OfType<JProperty>()
.Where(p => p.Name == "values")
.Select(p => p.Value.ToObject<List<KeyValuePair<string, string>>>())
.Select(values => new Book { Name = values.Find(v => v.Key == "name").Value, BookId = values.Find(v => v.Key == "bookid").Value })
.ToList();

Overriding .NET MVC's automatic JSON deserialization with ServiceStack

I'm trying to implement usage of ServiceStack for faster JSON serialization/deserialization in my .NET MVC4 application.
I have following for deserializing AJAX requests:
Global.asax.cs:
// Remove and JsonValueProviderFactory and add JsonServiceStackValueProviderFactory
ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new JsonServiceStackValueProviderFactory());
public sealed class JsonServiceStackValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
return null;
var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream).BaseStream;
return new DictionaryValueProvider<object>(
ServiceStack.Text.JsonSerializer.DeserializeFromStream<Dictionary<string, object>>(reader).AsExpandoObject(),
CultureInfo.CurrentCulture);
}
}
// http://nine69.wordpress.com/2013/02/02/asp-net-mvc-series-high-performance-json-parser-for-asp-net-mvc/
public static class CollectionExtensions
{
public static ExpandoObject AsExpandoObject(this IDictionary<string, object> dictionary)
{
var epo = new ExpandoObject();
var epoDic = epo as IDictionary<string, object>;
foreach (var item in dictionary)
{
bool processed = false;
if (item.Value is IDictionary<string, object>)
{
epoDic.Add(item.Key, AsExpandoObject((IDictionary<string, object>) item.Value));
processed = true;
}
else if (item.Value is ICollection)
{
var itemList = new List<object>();
foreach (object item2 in (ICollection) item.Value)
if (item2 is IDictionary<string, object>)
itemList.Add(AsExpandoObject((IDictionary<string, object>) item2));
else
itemList.Add(AsExpandoObject(new Dictionary<string, object> {{"Unknown", item2}}));
if (itemList.Count > 0)
{
epoDic.Add(item.Key, itemList);
processed = true;
}
}
if (!processed)
epoDic.Add(item);
}
return epo;
}
}
I am trying to deserialize an object which looks like:
[DataContract]
public class PlaylistItemDto
{
[DataMember(Name = "playlistId")]
public Guid PlaylistId { get; set; }
[DataMember(Name = "id")]
public Guid Id { get; set; }
[DataMember(Name = "sequence")]
public int Sequence { get; set; }
// Store Title on PlaylistItem as well as on Video because user might want to rename PlaylistItem.
[DataMember(Name = "title")]
public string Title { get; set; }
[DataMember(Name = "video")]
public VideoDto Video { get; set; }
[DataMember(Name = "cid")]
public string Cid { get; set; }
public PlaylistItemDto()
{
Id = Guid.Empty;
Title = string.Empty;
}
public static PlaylistItemDto Create(PlaylistItem playlistItem)
{
PlaylistItemDto playlistItemDto = Mapper.Map<PlaylistItem, PlaylistItemDto>(playlistItem);
return playlistItemDto;
}
public static List<PlaylistItemDto> Create(IEnumerable<PlaylistItem> playlistItems)
{
List<PlaylistItemDto> playlistItemDtos = Mapper.Map<IEnumerable<PlaylistItem>, List<PlaylistItemDto>>(playlistItems);
return playlistItemDtos;
}
}
[DataContract]
public class VideoDto
{
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "title")]
public string Title { get; set; }
[DataMember(Name = "duration")]
public int Duration { get; set; }
[DataMember(Name = "author")]
public string Author { get; set; }
[DataMember(Name = "highDefinition")]
public bool HighDefinition { get; set; }
public VideoDto()
{
Id = string.Empty;
Title = string.Empty;
Author = string.Empty;
}
/// <summary>
/// Converts a Domain object to a DTO
/// </summary>
public static VideoDto Create(Video video)
{
VideoDto videoDto = Mapper.Map<Video, VideoDto>(video);
return videoDto;
}
public static List<VideoDto> Create(IEnumerable<Video> videos)
{
List<VideoDto> videoDtos = Mapper.Map<IEnumerable<Video>, List<VideoDto>>(videos);
return videoDtos;
}
}
The first level is deserialized correctly, but the Video object is null. I have been scouring the web for additional documentation, but haven't come up with much. I've found these articles which seem to be relevant:
How to get ServiceStack to serialize / deserialize an expando object with correct types
Deserializing JSON into Object
But the first doesn't provide an example -- only mentions some changes to the new versions of ServiceStack. The second shows how to deserialize, but doesn't handle a Stream or ExpandoObjects. The first hints that ExpandoObjects might not be fully supported.
I feel like I am trying to deserialize a pretty simple object. Is there any support for nested levels of objects in ServiceStack? Thanks
You can see full source on GitHub if that is helpful: https://github.com/MeoMix/StreamusServer/blob/development/Streamus/Global.asax.cs

Categories