I have the following dotnet core code and I'm trying to return a TestResponse JSON object that has a few nodes under it. However, using the return Enumerable.Range(1, 4).Select(index => new Entities.TestResponse call in the post return for some reason all the attributes of response are not found in the enclosure when clearly Entities.TestResponse has the response definition. I'm probably not configuring the Enumerable enclosure correctly. Does anyone know how to resolve this, so I can set the response.result & response.exception and return response JSON from my REST POST method?
namespace TestApi.Entities
{
public class TestResponse
{
public TestResponseNodes response { get; set; }
}
public class TestResponseNodes
{
public string result { get; set; }
public string exception { get; set; }
}
}
[HttpPost]
public Task<IEnumerable<Entities.TestResponse>> Post([FromBody] String input)
{
return Enumerable.Range(1, 4).Select(index => new Entities.TestResponse
{
response.result = "No Error",
response.exception = "None"
}).ToArray();
}
Your syntax is wrong, you need to also new up the inner object, for example:
new Entities.TestResponse
{
response = new Entities.TestResponseNodes
{
result = "No Error",
exception = "None"
}
}
As an aside, you should follow common C# conventions and capitalise your property names, for example:
public class TestResponse
{
public TestResponseNodes Response;
}
public class TestResponseNodes
{
public string Result { get; set; }
public string Exception { get; set; }
}
Does Newtonsoft.JSON library have a simple way I can automatically deserialize JSON into 2 different Models/classes?
For example I get the JSON:
[{
"guardian_id": "1453",
"guardian_name": "Foo Bar",
"patient_id": "938",
"patient_name": "Foo Bar",
}]
And I need de-serialize this to the following models:
class Guardian {
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public int Name { get; set; }
}
class Patient {
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public int Name { get; set; }
}
Is there a simple way to deserialize this JSON into 2 Models without having to iterate over the JSON? Maybe JSON property ids will just work?
Pair<Guardian, Patient> pair = JsonConvert.DeserializeObject(response.Content);
First off, your models are slightly incorrect. The name properties need to be strings, instead of integers:
class Guardian
{
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public string Name { get; set; } // <-- This
}
class Patient
{
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public string Name { get; set; } // <-- This
}
Once you've corrected that, you can deserialize the JSON string into two lists of different types. In your case, List<Guardian> and List<Patient> respectively:
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var guardians = JsonConvert.DeserializeObject<List<Guardian>>(json);
var patients = JsonConvert.DeserializeObject<List<Patient>>(json);
It you want to do it with 1 call, you need to create a class that matches the JSON. That class can then return Guardian and Patient objects as needed. Also you'll need to use an array or list for the return type because the source JSON is an array.
The class to create:
public class Pair
{
public Pair()
{
Guardian = new Guardian();
Patient = new Patient();
}
[JsonIgnore]
public Guardian Guardian { get; set; }
[JsonIgnore]
public Patient Patient { get; set; }
[JsonProperty(PropertyName = "guardian_id")]
public int GuardianID
{
get { return Guardian.ID; }
set { Guardian.ID = value; }
}
[JsonProperty(PropertyName = "guardian_name")]
public string GuardianName
{
get { return Guardian.Name; }
set { Guardian.Name = value; }
}
[JsonProperty(PropertyName = "patient_id")]
public int PatientID
{
get { return Patient.ID; }
set { Patient.ID = value; }
}
[JsonProperty(PropertyName = "patient_name")]
public string PatientName
{
get { return Patient.Name; }
set { Patient.Name = value; }
}
}
And how to use it:
var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);
if (pairs.Any())
{
var pair = pairs[0];
Console.WriteLine(pair.Guardian.Name);
Console.WriteLine(pair.Patient.Name);
}
Not in one call, and it seems the data is an array, so you need a little more work.
Zip is the key method here to join the two separate object lists:
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
var combined = guardians.Zip(patients, (g, p) => Tuple.Create(g, p)).ToList();
It would be far more easier to just read the JSON at once, it a single object.
It can't be done with 1 call with the types that you show. You can try using the generic <T> approach for each type, also you'll need to use arrays or lists for the return type because the source JSON is an array:
var guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
var patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
And then combine the two if you need them to be paired. E.g. if you are sure that you always have just one of each:
var pair = new Pair(guardians[0], patients[0]);
You could make a type to house the two subobjects:
[JsonConverter(typeof(GuardianPatientConverter))]
class GuardianPatient
{
public Guardian Guardian { get; set; }
public Patient Patient { get; set; }
}
And then create a JSON converter to handle the JSON:
class GuardianPatientConverter : JsonConverter
{
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return typeof(GuardianPatient) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var jObject = JObject.Load(reader);
var guardian = new Guardian();
var patient = new Patient();
serializer.Populate(jObject.CreateReader(), guardian);
serializer.Populate(jObject.CreateReader(), patient);
return new GuardianPatient()
{
Guardian = guardian,
Patient = patient
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you can use it like so:
var json = "[{\"guardian_id\":\"1453\",\"guardian_name\":\"Foo Bar\",\"patient_id\":\"938\",\"patient_name\":\"Foo Bar\",}]";
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json);
and if you want it as an array of pairs:
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json)
.Select(o => new Pair(o.Guardian, o.Patient))
.ToArray();
This won't make it any faster, but I suspect you're looking for an easier way to work with the JSON.
One another approach would be creating class that matches JSON format, i.e. class with four properties with corresponding names. Then, deserialize JSON into that class and then use it in your code (set properties of objects with values from JSON, pass deserialized object to constructor of another class).
In your models, The name properties need to be strings, instead of integers. After correcting it.
You can use Tuple class
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var combination = new Tuple<List<Guardian>, List<Patient>>(JsonConvert.DeserializeObject<List<Guardian>>(json), JsonConvert.DeserializeObject<List<Patient>>(json));
static void Main(string[] args)
{
string json = JsonConvert.SerializeObject(new[]
{
new
{
guardian_id = "1453",
guardian_name = "Foo Bar",
patient_id = "938",
patient_name = "Bar Foo",
}
});
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(json);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(json);
}
Since both your objects are the same, wouldn't it make more sense to just have an ID/Name structure of a single base class? If you need to send all the data at the same time, you can restructure your data and use a data transfer object pattern. The JSON object would become
[{
"guardian": {
"id": "1453",
"name": "Foo Bar"
},
"patient": {
"id" : "938",
"name": "Foo Bar"
}
}]
And your corresponding data objects would be:
public class Record {
public int id { get; set; } // or string. I'm not sure which would be more appropriate
public string name { get; set;}
}
and
public class RecordDto {
public Record guardian { get; set; }
public Record patient { get; set; }
}
And your API would receive a
List<RecordDto>
parameter (since you are passing an array of objects).
I am working on WCF and I want to get record list array date wise and I need array key as the date which has common in record like below:
{
"EventAppGetAllSessionByCustomerIdResult":{
"02/22/2017":[
{
"SessionDate":"02/22/2017"
}
],
"08/27/2016":[
{
"SessionDate":"08/27/2016"
}
],
"Status":{
"Description":"Successfull!",
"Status":1
}
}
}
Basically, I want to extract values of SessionDate.
I assumed that you want to extract "SessionDate" property from your JSON. I recommend using JObject.Parse() method.
JObject jObject = JObject.Parse(json);
var result = (JObject)jObject["EventAppGetAllSessionByCustomerIdResult"];
var dates = new List<string>();
foreach(JProperty prop in result.Properties())
{
if (prop.Name != "Status")
{
var values = jObject["EventAppGetAllSessionByCustomerIdResult"][prop.Name].Values<string>("SessionDate");
dates.AddRange(values);
}
}
Little explanation:
In your case "02/22/2017" is property which has an array of objects. Each object has "SessionDate" property which holds value. So, following line will extract values from "SessionDate" of all objects:
var values = jObject["EventAppGetAllSessionByCustomerIdResult"][prop.Name].Values<string>("SessionDate");
values represents all dates from a single property. In your case, it can be from "02/22/2017" or from "08/27/2016".
dates will be list of "SessionDate" values. Of course, you have to handle possible exceptions by yourself.
I'm not sure its what you want but try this as your output object:
public class Session
{
public string SessionDate { get; set; }
}
public class Status
{
public string Description { get; set; }
public int Code { get; set; }
}
public class EventAppGetAllSessionByCustomerIdResult
{
public KeyValuePair<string, Session[]>[] EventAppGetAllSessionByCustomerId { get; set; }
public Status Status { get; set; }
}
I'm making my first simple project that uses relationships, a pretty simple checklist app. I'm trying to save a ItemList with title, and it contains a collection of items (Item name and item qty) that I'm using a different model for. When I try to post from my form, I get the following modelstate error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'BringIt.Models.Item' because the type requires a JSON object (e.g.
{"name":"value"}) to deserialize correctly.
To fix this error either
change the JSON to a JSON object (e.g. {"name":"value"}) or change the
deserialized type to an array or a type that implements a collection
interface (e.g. ICollection, IList) like List that can be
deserialized from a JSON array. JsonArrayAttribute can also be added
to the type to force it to deserialize from a JSON array. ↵Path
'item', line 1, position 46." Name
I've tried to change the types in my VM and models, but can't get past this error. Here is my existing code:
Models:
namespace BringIt.Models {
public class ItemList {
public int Id { get; set; }
public string Title { get; set; }
public DateTime EventDate { get; set; }
public ICollection<Item> Items { get; set; }
}
}
namespace BringIt.Models {
public class Item {
public int Id { get; set; }
public string ItemName { get; set; }
public int ItemQty { get; set; }
public string Person { get; set; }
public ItemList ItemList { get; set; }
}
}
My VM
namespace BringIt.ViewModels {
public class AddItemsVM {
public Item item { get; set; }
public ItemList itemList { get; set; }
}
}
Client Side Controller
export class CreateItemListController {
public itemList = null;
public item;
public items = [];
public addNew() {
var item = {};
this.items.push(item);
}
public save() {
this.itemListService.save(this.itemList, this.items).then(() => { this.$location.path('/') });
}
constructor(
private itemListService: MyApp.Services.ItemListService,
private $location: angular.ILocationService
) {
Server Side Controller
namespace BringIt.API
{
public class ItemListController : ApiController {
private IEFRepository _repo;
public ItemListController(IEFRepository repo) {
this._repo = repo;
}
public IHttpActionResult Post(AddItemsVM data) {
if (!ModelState.IsValid) {
return BadRequest(this.ModelState);
}
var itemList = data.itemList;
var item = data.item;
_repo.SaveItem(itemList, item);
return Created("", itemList);
}
}
}
Repo
public void SaveItem(ItemList listToSave, Item items) {
if (listToSave.Id == 0) {
listToSave.EventDate = DateTime.Now;
_db.ItemLists.Add(listToSave);
_db.Items.Add(items);
_db.SaveChanges();
} else {
var original = _db.ItemLists.Find(listToSave.Id);
original.Title = listToSave.Title;
original.EventDate = listToSave.EventDate;
original.Items = listToSave.Items;
_db.SaveChanges();
}
}
}
The client-side service
namespace MyApp.Services {
export class ItemListService {
private ItemListResource;
public save(itemList, items) {
debugger;
var data: any = {}
data.itemList = itemList;
data.item = items;
return this.ItemListResource.save(data).$promise;
}
constructor($resource: angular.resource.IResourceService) {
this.ItemListResource = $resource('/api/itemList/:id');
}
}
angular.module('MyApp').service('itemListService', ItemListService);
Thanks for all help. I've been at this for too many hours and just can't crack it.
From the client you'll be sending a JSON message like:
{
"itemList": {},
"item": [{blah}, {blah}... ]
}
where item is a JSON array.
Your C# view model doesn't have an enumerable item property. The deserializer cannot assign an array of items to non-enumerable property. The error message you receive is accurate, but a little confusing if you're not used to the terminology!
I might recommend you remove some of the DB code from the question, because that's a different matter entirely.
My application is binding a REST API, that returns this to me:
{
key: "XXXX-XXXX",
fields: {
customfield_10913: {
value: "L2"
}
}
}
I'm using Newtonsoft JSON to serialize and deserialize and I've created these models to make it work:
public class Issue
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
public class Fields
{
[JsonProperty("customfield_10913")]
public CustomField Level { get; set; }
}
public class CustomField
{
[JsonProperty("value")]
public string Value{ get; set; }
}
The application is deserializing everything ok, using this code:
T model = JsonConvert.DeserializeObject<T>(result);
After a lot of business logic, my WEB API should return a new JSON:
protected T Get()
{
return model;
}
And I've got everything like the JSON I've read from another API.
So, What I need?
I need to read the field CUSTOM_FIELDXXX, but I can't return it with this name in my WEB API. How could I read this field, but when I'm doing the serialization, it assume another one?
You may try below function
Issue model = deserializeObject<Issue>(result);
public T deserializeObject<T>(string result)
{
try
{
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
var items = (T)JsonConvert.DeserializeObject(result, typeof(T), settings);
return items;
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
}
If you can have any property name for your CustomField property, you can serialize that as a Dictionary<string, CustomField>, like so:
public class Issue
{
public Issue()
{
this.Fields = new Dictionary<string, CustomField>();
}
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("fields")]
public Dictionary<string, CustomField> Fields { get; set; }
}
Then you can use any string-valued name for your custom field.
Working fiddle.