get json object and parse it into a C# object - c#

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.

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

How to custom mapping fields name or type for method MongoDB.Bson.Serialization.BsonSerializer.Deserialize

I used the method MongoDB.Bson.Serialization.BsonSerializer.Deserialize() to deserialize from MongoDB.Bson.BsonDocument to MyType. But the method always meet System.FormatException since fields in MyType are not 100% match to the fields in BsonDocument.
I've tried to convert a complex json object(let's called mobj) from MongoDB(query result) to C# object(let's called csobj), so that I could deal with the data. The defualt datatype in csobj I use is string. But the mobj is too complex and we know it's schema less.
Once meet datatype like BinData(0,""), BinData(1,""), BinData(2,""), ISODate("") etc, in mobj, the System.FormatException may happen.
Once there are extra new fields in mobj, the System.FormatException may happen.
Once there are space in the field name like "Page one" : "XXXX", the the System.FormatException may happen and I don't know how to fix it till now.
var client = new MongoClient("mongodb://xxxxxx");
var database = client.GetDatabase("xxxxxxxxxx");
var collection = database.GetCollection<BsonDocument>("xxxxxxxxxx");
var results = await collection.Aggregate<BsonDocument>(filterBsonDocumentArray).ToListAsync();
foreach (var doc in results)
{
var model = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<MyType>(doc); // always meet exception here
}
Exception examples:
(mongodb datatype could not map with string)
System.FormatException: An error occurred while deserializing the Id property of class MongoQueryDemo.MyType: Cannot deserialize a 'String' from BsonType 'Binary'. ---> System.FormatException: Cannot deserialize a 'String' from BsonType 'Binary'.
(_id in mongo could not found UserId in C# object auto)
System.FormatException: Element '_id' does not match any field or property of class MongoQueryDemo.MyType.
My questions are list here:
Is there any way to tell the Deserializer, please be case insensitive;
Is there any way to customize the mapping the field name from mobj to csobj, like define "_id" --> UserId, "Ip Addr" --> "IpAddr";
Is there any way to customize the datatype, let the datatype BinData(0,""), BinData(1,""), BinData(2,""), ISODate("") are all could be convert into string without System.FormatException;
Is there any way to dealing whole complex sub-object mapping to C# string regardless its fields? Since its dynamic in schema less mongodb and I could not predefine any unknown field in the sub-ojbects.
This attribute will map field names from mobj to csojo `[BsonElement(#"52WeekChange")]
You have a few options, which are explored here, in the docs. (The following examples come from there.)
You can use a ClassMap:
BsonClassMap.RegisterClassMap<MyClass>(cm => {
cm.MapProperty(c => c.SomeProperty);
cm.MapProperty(c => c.AnotherProperty);
});
If you prefer to define mappings in your c# type declaration, you can use attributes.
BsonContructor can be used to map properties during object construction:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
// You can use this on one or more overloads as well:
[BsonConstructor]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
BsonElement is a property-level solution:
public class MyClass {
// The first parameter ("sp") is an optional mongodb field name mapping.
[BsonElement("sp")]
public string SomeProperty { get; set; }
}

How to deserialize json and retrieve specific property values in c#

I'm quite new to JSON with C# (Using VS2017). Tried accessing each element of this object via code (e.g. Getting the strings "Obj1", "Obj2", "Obj3" and then the values of each of their members (Id and name).
I do not know in advance how many "ObjX" there will be or their names. I'm trying to load this list into some class and then convert it into a CSV (or SQL inserts).
Tried with JSON.net and JsonFx but I think my skills are just not strong enough to understand how to do this other than brute-force string manipulation functions. Can anyone help?
{
"OBJ1":{
"id":1,
"name":"Name1",
},
"OBJ2":{
"id":2,
"name":"Name2",
},
"OBJ3":{
"id":3,
"name":"Name3",
}
}
Create a class, MyClass with two properties, int Id and string Name.
public class MyClass
{
public int Id {get; set;}
public string Name {get;set;}
}
Then, depending on how you want to do it you can either deserilize it to a Dictionary or create a MyClassRoot object with three MyClass properties.
I recommend the Dictionary approach.
If you use the Dictionary approach your code will still work if more properties gets added to the JSON. If you use the MyClassRoot solution you will need to add the corresponding property to the MyClassRoot if the json updates.
Then with JSON.Net you can deserialize the object like this.
var result = JsonConvert.DeserializeObject<Dictionary<string, MyClass>>(json);
The "OBJ1", "OBJ2" and so on will then be keys in the dictionary and you can access the values like this:
var obj1 = result["OBJ1"];
var obj1Name = obj1.Name;
var obj1Id = obj1.Id;
To get all the MyClass objects to a list, simply do the following:
var list = result.ToList();
MyClassRoot approach(not recommended at all, just a POC):
public class MyClassRoot
{
public MyClass Obj1 {get;set;}
public MyClass Obj2{get;set;}
public MyClass Obj3{get;set;}
}
var result = JsonConvert.DeserializeObject<MyClassRoot>(json);
var obj1Name = result.Obj1.Name;
var obj1Id = result.Obj1.Id;

C# Parse/Deserialize JSON partially with Newtonsoft

I have to extract a part of json-string using .net or newtonsoft json.
JSON:
var json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":\"SOME_CUSTOM_JSON_OBJECT_DIFFERENT_FOR_EACH_METHOD\",\"subtrahend\":23}}";
C# Class:
class MyJson{
public string method { get; set; }
//public string parameters {get; set;}
public object parameters {get; set;}
}
I do not need to parse all the children of "parameters" json-object. "parameters" could be a very big object ([{obj1}...{obj1000}], objX of 1000 fields), parse which would be not performant.
I would like i.e. to pass it exactly as it is on some point, so conversion "string-C#object-string" would be redundant.
I do not want use Regexp or string transformations (string.Substring, Split and co), because of error margin, I know that all .net and newtonsoft string transformations based.
Question 1: if I define a property of type "object", how newtonsoft will handle this? (Documentation is worse than msdn, so I'm looking for the input from you, who already tried this).
static void Main(string[] args)
{
var json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":42,\"subtrahend\":23}}";
var data = JsonConvert.DeserializeObject<MyJson>(j);
// what internal representaion of data.parameters?
// How is it actually converted from json-string to an C# object (JObject/JsonObject).
}
In perfect case:
"parameters" is a string and calling
ExtractMyJson(jsonString)
gives me the json string of parameters.
Basically I need the newtonsoft version of
string ExtractMyJson(jsonString){
var p1 = jsonString.Split(",");
// .. varios string transformations
return pParams;
}
Note: please don't reference "dynamic" keyword or ask why no string transformations, it's the very specific question.
If you know that your parameters are unique you can do something like this:
class MyJson
{
public string method { get; set; }
public Dictionary<string,object> parameters { get; set; }
}
................
string json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":{\"img\": 3, \"real\": 4},\"subtrahend\":23}}";
var data = JsonConvert.DeserializeObject<MyJson>(json);
If you let it as object is going to receive the type Newtonsoft.Json.Linq.JObject.
Have you tried JTOKEN?
It is a rather simple solution to partially read basic or nested JSONs as described in this post.
For a nested JSON
{
"key1": {
"key11": "value11",
"key12": "value12"
}
"key2": "value2"
}
it would look like this
JToken token = JToken.Parse(json);
var value12 = token.SelectToken("key1.key12");
to get the element of the key "key12.
I think this could go nicely with your problem.
Well Objects are treated the same way your parent object is treated. It will start from the base of the graph. So if you have something like:
Person
{
Address Address {get;set;}
}
The Json will start Deserializing Address and then add in the Person object.
If you want to limit thesize of the graph depth you can use a setting like :
JsonConvert.DeserializeObject<List<IList<IList<string>>>>(json, new JsonSerializerSettings
{
MaxDepth = 2
});
For more configurations of the JsonSerializer check JsonSerializerSettings
If your field is an object then that object will have the KeyValuePair of every property that it holds, based on that when you cast that field you can access that type.(the behaviour is the same as assigning a type to an object in C#).
Update: So if you question using JsonObject or type, well JObject is and intermediary way to construct the json format in a generic format. But using the Type deserializatin means you can ignore properties you are not interested in. Mapping to a json with a type makes more sense because it creates a new object and dismisses the old JObject.

Deserializing a mixed list of objects from JSON

I'm using the DataContractJsonSerializer to deserialize objects from an external service. In most cases, this has worked great for me. However, there is one case where I need to deserialize JSON that contains a list of objects that all inherit from the same base class, but there are many different types of objects in that list.
I know that it can be done easily by including a list of known types in the serializer's constructor, but I don't have access to the code that generated this JSON service. The types that I'm using will be different from the types used in the service (mostly just the class name and namespace will be different). In other words, the classes that the data was serialized with will not be the same classes that I'll use to deserialize it even though they'll be very similar.
With the XML DataContractSerializer, I can pass in a DataContractResolver to map the services types to my own types, but there is no such constructor for the DataContractJsonSerializer. Is there any way to do this? The only options that I've been able to find are: write my own deserializer, or use Microsoft's JsonObject that isn't tested and "should not be used in production environments."
Here is an example:
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
class Program
{
static void Main(string[] args)
{
var jsonStr = "[{\"__type\":\"Student:#UnknownProject\",\"Name\":\"John Smith\",\"StudentId\":1},{\"Name\":\"James Adams\"}]";
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(jsonStr);
writer.Flush();
stream.Position = 0;
var s = new DataContractJsonSerializer(typeof(List<Person>), new Type[] { typeof(Student), typeof(Person) });
// Crashes on this line with the error below
var personList = (List<Person>)s.ReadObject(stream);
}
}
}
Here is the error mentioned in the comment above:
Element ':item' contains data from a type that maps to the name
'http://schemas.datacontract.org/2004/07/UnknownProject:Student'. The
deserializer has no knowledge of any type that maps to this name. Consider using
a DataContractResolver or add the type corresponding to 'Student' to the list of
known types - for example, by using the KnownTypeAttribute attribute or by adding
it to the list of known types passed to DataContractSerializer.
I found the answer. It was very simple. I just needed to update my DataContract attribute to specify which namespace (you can also specify a different name) they map to in the source JSON like this:
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject")]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject"]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
That JsonObject was a sample for .NET 3.5. There is a project in codeplex - http://wcf.codeplex.com - which has a tested implementation of the JsonValue/JsonObject/JsonArray/JsonPrimitive classes, including source code and unit tests. With that you can parse "untyped" JSON. Another well-used JSON framework is the JSON.NET at http://json.codeplex.com.
You may create a DTO before serializing.
I use a class like: (pseudo code)
class JsonDto
string Content {get;set;}
string Type {get;set;}
ctor(object) => sets Content & Type Properties
static JsonDto FromJson(string) // => Reads a Serialized JsonDto
// and sets Content+Type Properties
string ToJson() // => serializes itself into a json string
object Deserialize() // => deserializes the wrapped object to its saved Type
// using Content+Type properties
T Deserialize<T>() // deserializes the object as above and tries to cast to T
Using the JsonDto you can easily serialize arbitrary objects to JSON and deserialize them to their common base type because the deserializer will always know the original type and returns an type of object reference which will be casted if you use the generic Deserialize<T> method.
One caveat: If you set the Type property you should use the AssemblyQualifiedName of the type, however without the version attribute (ex: MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly). If you just use the AssemblyQualifiedName property of the Type class you will end up with errors if your assembly version changes.
I implemented a JsonDtoCollection the same way, which derives from List<JsonDto> and provides methods to handle collections of objects.
class JsonDtoCollection : List<JsonDto>
ctor(List<T>) => wraps all items of the list and adds them to itself
static JsonDtoCollection FromJson(string) // => Reads a collection of serialized
// JsonDtos and deserializes them,
// returning a Collection
string ToJson() // => serializes itself into a json string
List<object> Deserialize() // => deserializes the wrapped objects using
// JsonDto.Deserialize
List<T> Deserialize<T>() // deserializes the as above and tries to cast to T

Categories