Do I need to deserialize the query result from DocumentDb? - c#

I have a simple POCO for person object that looks like this:
public class Person
{
public int PersonId {get; set;}
public string FirstName {get; set;}
public string MiddleName {get; set;}
public string LastName {get; set;}
public char Gender {get; set;}
}
I have the following code that queries the People collection in my DocumentDb database.
private async static Task<List<Person>> GetPeopleList(string colSelfLink)
{
dynamic doc = client.CreateDocumentQuery<Document>(colSelfLink, "SELECT p.PersonId, p.FirstName, p.MiddleName, p.LastName, p.Gender FROM People p").AsEnumerable().FirstOrDefault();
List<Person> peopleList = new List<Person>();
if (doc != null)
{
peopleList = doc;
}
return peopleList;
}
When I run this code, I'm getting the following error:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyApp.Person]' 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 'PersonId', line 1, position
48.
How do I convert the JSON object coming from my query to a Person object? In my MVC app, the model binder does a pretty good job but this is running in a separate class library. How should I approach converting this JSON object?

This is what I did and it works. I'd appreciate other answers though if there's a better way.
private async static Task<List<Person>> GetPeopleList(string colSelfLink)
{
dynamic doc = client.CreateDocumentQuery<Document>(colSelfLink, "SELECT p.PersonId, p.FirstName, p.MiddleName, p.LastName, p.Gender FROM People p").AsEnumerable().ToList();
List<Person> peopleList = new List<Person>();
if (doc != null)
{
Person person;
foreach(var item in doc)
{
person = JsonConvert.DeserializeObject<Person>(item.ToString());
peopleList.Add(person);
}
}
return peopleList;
}

Anthony Chu was on the right track with this one. The simple solution is:
private async static Task<List<Person>> GetPeopleList(string colSelfLink)
{
return client.CreateDocumentQuery<Person>(colSelfLink, "SELECT * FROM People").ToList();
}
This will automatically deserialise each returned record and turn it into a People object.

I had this exact same problem, only that my list of objects failing, was nested in another object (document).
public List<MyObject> MyProperty { get; set; }
I am so happy right now that I found the solution:
If you have lists of complex objects in your document, put this on the properties of the complex object:
[JsonProperty(PropertyName = "yourPropertyName")]
This is well known to be needed on the primary object (document), haven't needed them on nested objects though, until they were in a list.

Related

Why self reference loop cannot be deserialized? [It can be deserialized]

Looks like no C# JSON serialization framework can deserialize self reference loop.
But why?
Let's imagine that we have this class structure:
public class Person
{
public string Name { get; set; };
public City City { get; set; }
}
public class City
{
public string Name { get; set; }
public List<Person> Persons { get; set; }
}
and in serialization/deserialization settings we say that if objects are the same put $ref instead of serializing object again
and serialized person will look like this
{
"$id":"1",
"Name":"John",
"City":{
"$id":"2",
"Name":"London",
"Persons":[
{
"$ref":"1"
}
]
and on deserialization you are simply creating person with no City then creating City with person and initialize person property with created City
var person = new Person();
var city = new City();
city.Persons = new List<Person>{ person };
person.City = city;
One explanation I can find was that if you have no empty constructors, you need to create parameters first.
But even if you have one, all serializing frameworks that I can find cannot deserialize it properly.
EDIT:
IT CAN BE DESERIALIZED.
I thought that structure in the example is according to my real class structure, my real structure was not deserializing properly, so I didn't try to really serialize/deserialize structure from the example. Then, when I tried it was deserialized fine, so I checked my real structure and the reason why it was not deserializing properly is that I missed setter on property.
You didn’t show any actual attempts to serialize/deserialize so I don’t know what you’ve tried, but it took me 30s to find out how to do it with System.Text.Json so I suggest you give that a try. It involves configuring the ReferenceHandler option in JsonSerializerOptions
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true
};
Microsoft page devoted to this topic

Json Array cannot be deserialized

I would like to start by saying that I am not a developer and this is my very first time writing a code to this extend of complication (at least to me). Any help/guidance would be much appreciated.
The idea of this program is to retrieve the employee user ID (or signature) from an API URL once the name has been entered.
I have a JSON String
[{"signature":"JDOW","firstName":"Jane","fullName":"Dow, Jane","lastName":"Dow"}]
I am trying to deserialize it to a collection. But I am getting an error. Can somebody direct me to the right way to fix this?
namespace TimeSheet_Try11_Models
{
public class Employeename
{
[JsonProperty("Signature")]
public string Signature { get; set; }
[JsonProperty("FirstName")]
public string FirstName { get; set; }
[JsonProperty("FullName")]
public string FullName { get; set; }
[JsonProperty("LastName")]
public string LastName { get; set; }
}
}
I a trying to convert using the following code:
uri = StaticStrings.UrlIora + name;
var response = wc.DownloadString(uri);
Employeename status = JsonConvert.DeserializeObject<Employeename>(response);
The error I am getting is:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'TimeSheet_Try11_Models.Employeename' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
Deserialize the Json data for collection as:
var status = JsonConvert.DeserializeObject<List<Employeename>>(response);
or
List<Employeename> status = JsonConvert.DeserializeObject<List<Employeename>>(response);
Another thing is that, no need to use attribute [JsonProperty("anyProperty")] with your properties. Json can be deserialized without using it in attributes.
After getting the deserialzed data in status object, any value can be fetched from that object as:
string signature = status.Select(js => js.Signature).First();
Similarly other values can also be taken from the status.
Your JSON string has square brackets ([]) around it, which means it is a collection of items.
This is why the Deserialiser is erroring.
You need to either remove those brackets to give just one employee object in the input string, or tell DeserializeObject that it is a List you are deserialising.
var converted = JsonConvert.DeserializeObject<List<Employeename>>(response);
Working Fiddle here

How to convert object into json array without property names

For this class
class Customer {
public string FirstName {get; set;}
public string LastName {get; set;}
}
I have collection
List<Customer> customers
When returning to browser client
return new JsonResult(new
{
data = customers
});
The client get
{"data":[{"firstName":"Johny","lastName":"Johnson"}]}
Is there some way to get
{"data":[{"Johny","Johnson"}]}
without doing foreach like
var output = new List<string[]>();
foreach (var r in customers)
{
output.Add(new string[] {
r.FirstName,
r.LastName
});
}
?
You could add another property in the Customer object,
public string[] FullName {get { return new string[]{FirstName, LastName}; } }
Decorate your Firstname and LastName properties with [JsonIgnore] so they don't get serialized.
Final product would look like so
public class Customer{
[JsonIgnore]
public string FirstName{get;set;}
[JsonIgnore]
public string LastName{get;set;}
[JsonProperty("data")]
public string[] FullName {get { return new string[]{FirstName, LastName}; } }
public Customer(string FirstName, string LastName){
this.FirstName = FirstName;
this.LastName = LastName;
}
}
public static void Main(string[] args)
{
Customer c = new Customer("Adrian", "i6");
Console.Write(JsonConvert.SerializeObject(c));
}
This of course won't return exactly the desired result, if you wanted to completely remove the property you'll have to override the JsonWrite method inside JsonConverter however that would cause the JSON to be invalid of course as the JSON object requires a key:value property.
DotNetFiddle runnable of the above.
If you want some sort of "automatically derive a table from an array of JSON objects" functionality that's general across any data type, the algorithm would be to:
Iterate over the array, collecting newly-encountered property names into a list as you go into column names. This is the only way to get all property names since it may not be guaranteed that all objects have the same properties in JSON.
Create a list for each object in the list
Map each object's property value into the list index of the column corresponding to the property name
This will give you two output artifacts: the column listings and the values by index. If you are safe assuming that the first object has the same properties as all the other objects in the Array, then you can avoid iterating over the entire collection for the first step. This is untested, but hopefully you get the gist.
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
// ...
var payload = JObject.Parse(rawJson);
var dataArray = payload["data"] as JArray;
var firstItem = dataArray[0] as JObject;
var columns = firstItem.Properties().Select(prop => prop.Name).ToList();
var rows = (
from obj as JObject in dataArray
select columns.Select(col => obj[col]).ToList()
).ToList();

get json object and parse it into a C# object

json:
[{
"PersonsTable":[
{"id":293,"firstname":"jos","lastname":"don"},
{"id":1861,"firstname":"jef","lastname":"dan"},
{"id":1896,"firstname":"janine","lastname":"din"}]
}]
code:
List<Person> persons = new List<Person>();
dynamic dynObj = JsonConvert.DeserializeObject(response);
foreach (var data in dynObj.PersonsTable)
{
Person p = new Person(data.id, data.firstname, data.lastname);
persons.Add(p);
}
Object:
public class Person
{
public Person ()
{
}
public Person (string id, string firstname, string lastname)
{
this.id= id;
this.firstname = firstname;
this.lastname = lastname;
}
public string id{ get; set; }
public string firstname{ get; set; }
public string lastname{ get; set; }
}
I want to put the data under "PersonsTable" into the person list.
I have tried to achieve this with serialize and dynamic variables but i always get a weird error "Missing compiler required member, 'microsoft.CSharp.RUntimeBinder.CSharpArgumentINfo.Create'"..
The NuGet package itself i can't install because my project runs in .Net 3.5 (for some reason).
Can someone help me with my problem? Are there other ways to get a list of persons in result?
Your problem is not related to json parsing I think.
As you are using the keyword "dynamic", you must have in your project a reference to Microsoft.CSharp.dll.
See here for example : C# dynamic compilation and "Microsoft.CSharp.dll" error
update :
I see you have updated your question since I've posted my answer. You now say you are running in .Net 3.5.
To be clear, dynamic is NOT AVAILABLE in .Net 3.5. See Use of Dynamic Keyword in .Net 3.5 for example.
You have a few problems:
The names of the properties of your c# classes do not match the property names in the JSON. (Note - fixed in the edited version of your question.)
Your root JSON container is an array containing a single object, not the object itself. You need to account for the extra level of nesting when parsing.
You say you are running on .Net 3.5 which does not support dynamic.
Rather than using dynamic, you can explicitly parse to a JToken then manually map to your Person type using LINQ to JSON with SelectTokens():
var root = JToken.Parse(response);
var persons = root
// Use the JSONPath wildcard operator to select all entries in the "PersonsTable"
.SelectTokens("[*].PersonsTable[*]")
// And map the individual entry to a Person type.
.Select(data => new Person((string)data["id"], (string)data["firstname"], (string)data["lastname"]))
.ToList();
Even if you were able to use dynamic, by doing so you lose compile-time error checking. Using statically defined methods may lead to fewer unexpected run-time errors.
Sample fiddle.
Create new viewModel with field List PersonsTable {get; set;}, then accept it on endpoint, it will automatically map the model, altought you might have to add [JsonProperty(PropertyName = "id")], to your Person class members for proper mapping.

How to convert JSON Response to a List in C#?

This might be a basic question but I am stuck while converting a JSON Response to a List.
I am getting the JSON Response as,
{"data":[{"ID":"1","Name":"ABC"},{"ID":"2","Name":"DEF"}]}
Have defined a Class,
class Details
{
public List<Company> data { get; set; }
}
class Company
{
public string ID { get; set; }
public string Name { get; set; }
}
Have tried this for converting,
List<Details> obj=List<Details>)JsonConvert.DeserializeObject
(responseString, typeof(List<Details>));
But this returns an error, saying
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Client.Details]' 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.
Kindly help!
You don't have a List<Detail> defined in your JSON. Your JSON defines one Detail record, which itself has a list of companies.
Just deserialize using Details as the type, not List<Details> (or, if possible, make the JSON wrap the single detail record into a one item array).
You need to Deserialize like this:
var Jsonobject = JsonConvert.DeserializeObject<Details>(json);
using classes generated by json2csharp.com:
var Jsonobject = JsonConvert.DeserializeObject<RootObject>(json);
and your classes should be :
public class Datum
{
public string ID { get; set; }
public string Name { get; set; }
}
public class RootObject
{
public List<Datum> data { get; set; }
}
you can always use json2csharp.com to generate right classes for the json.
You can use JavaScriptDeserializer class
string json = #"{""data"":[{""ID"":""1"",""Name"":""ABC""},{""ID"":""2"",""Name"":""DEF""}]}";
Details details = new JavaScriptSerializer().Deserialize<Details>(json);
EDIT: yes, there's nothing wrong with OP's approach, and Servy's answer is correct. You should deserialize not as the List of objects but as the type that contains that List

Categories