Json Serializer with multiple JsonProperty Newtonsoft.Json [duplicate] - json

This question already has answers here:
multiple JsonProperty Name assigned to single property
(5 answers)
Closed 1 year ago.
I have an entity class with JsonProperty attribute as follows:
public class Book
{
[JsonProperty("id")]
public long BookId { get; set; }
[JsonProperty("name")]
public string BookName { get; set; }
}
I want to deserialize this from JSON string to the entity. Due to JsonProperty id & name, the json should have the id/name schema and in that case the deserialize works perfectly (as expected). But in some cases, the json string can also have BookId and BookName. Can I have multiple JsonProperty attributes to handle this? Or is there a way to let the serializer know, that if not id then deserialize using BookId? Or any other way to handle this use case?

You could solve this by defining alternative properties, which redirects to existing ones. For example,
public class Book
{
[JsonProperty("bookId")]
public long BookId { get; set; }
[JsonProperty("id")]
private long Id
{
get => BookId;
set => BookId = value;
}
[JsonProperty("bookName")]
public string BookName { get; set; }
[JsonProperty("name")]
private string Name
{
get => BookName;
set => BookName = value;
}
}
Demo

You can replace the property name before deserializing the json.
var book = #"
[
{
'id': 123456789,
'name': 'book1'
},
{
'BookId': 1234567,
'BookName': 'book2'
}
]
";
var after = book.Replace("BookId", "id").Replace("BookName", "name");
var sbook = JsonConvert.DeserializeObject<List<Book>>(after);
Then you can get the entire deserialization object.

Related

Cannot deserialize the current JSON object while posting relational data

Ok there are a lot of questions and answers with this, if you know an exact duplicate of this please point me there but I am too dumb to understand how to make it work.
I want to add a worker to a job and everything is good until reaches ModelState. Here are the steps I am doing.
Filling the form
I submit the form and console breakpoint.
Name = "test", Description = "test",
worker = {Id: 21, FirstName: "Will", LastName: "Smith", Job: null}
Angular service in console
jobs = {Name: "test", Description: "test", worker: {…}}
Not sure why the dots in the brackets.
Reaches API method and fails modelstate.
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
In browser console I get:
Message:"The request is invalid."
ModelState:job.worker.Id:["Cannot deserialize the current JSON object (e.g. {…N object. Path 'worker.Id', line 1, position 16."]
Full error message from ModelState is:
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type
'System.Collections.Generic.ICollection`1[TestRotaru.Models.Worker]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly. To fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize from a JSON object. Path 'worker.Id', line 1, position
51.
Alright so far this is like a duplicate question but I tried few things and I don't understand some things in answers.
I tried to decorate my models with attributes such as:
These on top of the class
//[Serializable]
//[DataContract(IsReference = true)]
//[JsonObject(MemberSerialization.OptIn)]
On properties
//[JsonProperty]
Now the thing I have no clue and I don't understand:
var dict = JsonConvert.DeserializeObject
<Dictionary<string, Item>>(*Where this string comes from*);
This is just an example, I am concerned about that string in brackets not about if is a Dictionary or something else.
Here is my method:
[ResponseType(typeof(Job))]
public IHttpActionResult PostJob(Job job)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
job.DueDate = DateTime.Now;
db.Job.Add(job);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = job.Id }, job);
}
In all the answers about this everyone has that random json string in the round brackets... From where I am supposed to have that?
Here are my models:
Job Model:
//[Serializable]
//[DataContract(IsReference = true)]
//[Serializable()]
//[JsonObject(MemberSerialization.OptIn)]
public class Job
{
[JsonProperty]
public int Id { get; set; }
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Description { get; set; }
[JsonProperty]
public DateTime DueDate { get; set; }
[JsonProperty]
public bool IsCompleted { get; set; }
//[JsonProperty]
//[System.Runtime.Serialization.IgnoreDataMember]
public virtual ICollection<Worker> Worker { get; set; }
}
Worker Model:
//[Serializable]
//[DataContract(IsReference = true)]
//[JsonObject(MemberSerialization.OptIn)]
public class Worker
{
[JsonProperty]
public int Id { get; set; }
[JsonProperty]
public string FirstName { get; set; }
[JsonProperty]
public string LastName { get; set; }
//[JsonProperty]
//[System.Runtime.Serialization.IgnoreDataMember]
public virtual ICollection<Job> Job { get; set; }
}
Angular component and service:
add(Name: string, Description: string, worker): void {
this.jobsServices.addJob({Name, Description, worker } as Jobs)
.subscribe(jobs => {
this.jobs.push(jobs);
});
}
addJob(jobs: Jobs): Observable<Jobs> {
return this.http.post<Jobs>(this.apiURL, jobs, httpOptions);
}
div class="row">
<div class="col-md-12">
<label class="labelInputs">Select Worker</label><br>
<select [(ngModel)]="worker" class="form-control">
<option [value]="0">Select Worker</option>
<option *ngFor="let worker of workers" [ngValue]="worker">{{worker.FirstName}} {{worker.LastName}}</option>
</select>
</div>
<button class="brn btn-lg btn-block btn-change" (click)="add(name.value, desc.value, worker)">Add Job</button>
Alright is a long question but I wanted to prove myself I did my research and I just didn't thrown the error message and ask for solutions but this is really getting on my nerves.
Since I am posting relational data and my console shows ModelState{job.worker.Id: [,…]} how do I deserialize this? Plus my ModelState shows the worker as null when reaches the API, I can guess because is not converted from json, but worth to know.
Thank you for help.
So, the issue is that on your SPA you are sending worker as a single object and your API expects a list (array) of workers. Now I don't know what you want.. but you can either fix it by changing your API model to expect a single worker or change your SPA to send a list of workers. That no one can answer you.. depends on your app's requirements. But, my best guess is that a Job might have several workers. So:
Api Models (mostly the same.. just removed the attributes and changed Worker to Workers):
public class Job
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime DueDate { get; set; }
public bool IsCompleted { get; set; }
public virtual ICollection<Worker> Workers { get; set; }
}
public class Worker
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// This is an "array". your spa needs to send in this format.
public virtual ICollection<Job> Job { get; set; }
}
Spa Code:
export class Job {
id: int;
name: string;
// other fields here..
// Workers property that maps API model
workers: Array<Worker>;
constructor(id: int, name: string [other fields..]) {
this.id = id;
// and so on..
}
public addWorker(worker) {
this.workers.push(worker);
}
}
export class Worker {
id: int;
firstName: string;
// other fields here.. No need for array of jobs here!
}
With this model, you can do this before calling the API (on add function):
add(Name: string, Description: string, worker: Worker): void {
// creates a job using constructor
const job = new Job(name, description);
// add Job via function(can even be more complex and add validation and so on..)
job.addWorker(worker);
this.jobsServices.addJob(job)
.subscribe(jobs => {
this.jobs.push(jobs);
});
}
This will call the api with a json like this:
{
"id": 1,
"name": "some name",
[ other fields..]
"workers": [
{ "id:" 1, "name": "some worker name" }
]
}

C# JSON data serialized and binded to DataGridView

I have this data class for storing data parsed from JSON formatted web data (using Json.NET library):
[Serializable()]
public class MovieData
{
public string FilePath { get; set; }
public string OrigName { get; set; }
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "year")]
public int Year { get; set; }
[JsonProperty(PropertyName = "genres")]
public string[] Genres { get; set; }
}
The next class is for to be able serialize collection of MovieData objects:
[Serializable()]
[XmlRoot("MovieCollection")]
public class MovieCollection
{
[XmlArray("Movies")]
[XmlArrayItem("Movie", typeof(Movie))]
public List<Movie> movies = new List<MovieData>();
}
Finally, I need to bind such a collection of MovieData to DataGridView (or single MovieData object to DataGridViewRow), like:
dgvMovies.DataSource = movieCollection.movies;
Is it possible to bind it without hard-setting of DataGridViewColumn collection before? Native data types are not problem, problem is string[] Genres array, which I need to format it in DataGridView in some way, like:
"genres[0] / genres[0] / ... genres[n]"
At this moment, while simply setting DataSource to collectin, this array is ignored (is not displayed anyway).
In MovieData class, you can add the following property :
public string GenresAsString
{
get { return String.Join("/", Genres); }
set { Genres = value.Split('/'); }
}
You will surely have to improve the setter to make it more resilient (triming, removing empty genres) if you plan to let the user modify this value.
Else you can remove the setter.

Is there a way to ignore default values during serialization with ServiceStack json?

I am using Entity Framework as my ORM. It has ComplexTypeAttribute (which is used to annotate POCO's). Properties that are complex types, are always instantiated (using default constructor), regardless of their value; And as a consequence, are always serialized by ServiceStack JsonSerializer (along with their properties).
JSON.NET has an enum called DefaultValueHandling, which can be used in these situations.
Does ServiceStack have something similar?
For example:
class Person
{
string Name { get; set; }
Address Address { get; set; }
}
[ComplexType]
class Address
{
string Street { get; set; }
int Number { get; set; }
int PostalCode { get; set; }
}
When I serialize a person that doesn't have address I get this:
"{ Name: Jim, Address : { Number: 0, PostalCode: 0 } }"
In Json.Net if I set DefaultValueHandling to Ignore, I only get
"{ Name: Jim }"
Yes, here are the different ways you can ignore properties with ServiceStack's JSON and Text serializers.
The serializers also support multiple hooks to customize serialization and deserialization.
The JsConfig class shows all the customizations that are possible.
Please consider changing your value types to nullable data types and set null as default value for any reference type.
class Person
{
string Name { get; set; }
Address Address { get; set; }
}
[ComplexType]
class Address
{
string Street { get; set; }
int? Number { get; set; }
int? PostalCode { get; set; }
}
This should help you get rid of attributes with default values as ServiceStack Text will omit properties with null value. Also observe that 'Age' which is of type int? is omitted from serialized output when it is null. The example also demonstrates serialization using anonymous objects.
Example below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServiceStack;
namespace JsonTest
{
class Person
{
public string Name { get; set; }
public string Address { get; set; }
public int? Age { get; set; }
public List<Person> Children { get; set; }
public Person()
{
Children = new List<Person>();
}
}
class Program
{
static void Main(string[] args)
{
var c1 = new Person { Name = "John", Address = "USA", Age = null };
var c2 = new Person { Name = "John", Address = "USA", Age = 12 };
List<Person> children = new List<Person>();
children.Add(c1);
string name = "Jim";
// Uncomment lines below and check - Children attribute is omitted from JSON result
// children = null;
// name = null;
var p1 = new { Name = name, Address = "USA", Age=40, Children = children};
var p2 = new Person { Name = "Jim", Address = "USA" , Age = null};
p2.Children.Add(c2);
Console.WriteLine(p1.ToJson());
Console.WriteLine(p2.ToJson());
Console.ReadLine();
}
}
}
Output:
{"Name":"Jim","Address":"USA","Age":40,"Children":[{"Name":"John","Address":"USA","Children":[]}]}
{"Name":"Jim","Address":"USA","Children":[{"Name":"John","Address":"USA","Age":12,"Children":[]}]}
{"Address":"USA","Age":40}
{"Name":"Jim","Address":"USA","Children":[{"Name":"John","Address":"USA","Age":12,"Children":[]}]}

DisplayName attribute is ignored while json conversion

I have a class as below
public class Person
{
public string Name { get; set; }
[DisplayName ("Please Enter Your Age")]
public int Age { get; set; }
public string Sex { get; set; }
}
I serialized this object to Json using json() of MVC3, but the DisplayName attribute is ignored. I get the json as
"*{"Name":"Person Name","**Age**":28,"Sex":"Male"}*"
Actually i was expecting
"*{"Name":"Person Name","**Please Enter Your Age**":28,"Sex":"Male"}*"
Code converts the object to json
[HttpGet]
public JsonResult JsonTest()
{
Person person = new Person();
person.Age = 28;
person.Name = "Person Name";
person.Sex = "Male";
return (Json(person, JsonRequestBehavior.AllowGet));
}
Any help would be appreciated!!!
You can use the DataContractJsonSerializer to give different names to your properties by using the [DataMember(Name = "myOwnName")] data annotation. Or write your own serializer.
Example can be found here.
Internally the Json method uses the JavaScriptSerializer class to serialize the class into a JSON string. It doesn't allow you to change property names. I guess you will have to roll your own JSON serialization routine. The question is: why do you need that?

Silverlight 4 DataContractJsonSerializer, private fields of a derived class

I use DataContractJsonSerializer to deserialize json data in Silverlight 4.
Json data key names do not match my class property names; so I guess I have to use
DataMemberAttribute. So I did the following:
[DataContract]
public class Person : Model
{
[DataMember(Name = "id")]
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
[DataMember(Name = "name")]
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Now deserialization fails because I didn't apply DataContractAttribute to Person's base class Model. Is it a strict requirement? Also, after I applied DataContractAttribute to Model, deserialization fails again, because I applied DataMember attributes to private fields, not to the public properties. Why can't I apply them to private members (the documentation seems to say otherwise).
NOTE: server-side code is not ASP.NET; so WCF isn't used.
In order to get the private members to serialize over WCF correctly, we had to change them all to protected internal instead of private. Maybe the same applies for DataContractJsonSerializer?