C# dynamic class - how to preserved the var name from serialize output - c#

The variable name with # prefix changed after serialize due C# naming. How to prevent this?
//assignment of object value with #Timestamp
List<dynamic> Documents = new List<dynamic>();
Documents.Add( new { Index = ""index-name-test", Type = "doc", Id = g.ToString(),
Title = "title1", #Timestamp = DateTime.UtcNow });
foreach (var doc in Documents)
{
var json = JsonConvert.SerializeObject(new { Documents= doc });
}
as in the json value, it contain
"{\"Documents\":{\"Index\":\"index-name-test*\",\"Type\":\"doc\",\"Id\":\"76134434-2ed0-48df-9034-841b386a0dbc\",\"Title\":\"title1\",\"Timestamp\":\"2019-04-14T15:50:33.596931Z\"}}"
{"Documents":{"Index":"index-name-test*","Type":"doc","Id":"76134434-2ed0-48df-9034-841b386a0dbc","Title":"title1","Timestamp":"2019-04-14T15:50:33.596931Z"}}
How to make Timestamp become #Timestamp ?

In C#, prefixing a variable name with # is just a way to allow you to use reserved words as variable names. For example, you can do this:
var #class = "foo";
If you don't use a # you will get a compiler error. As such, when you serialise your dynamic objects, the variable name is still Timestamp. The best option would be to use a concrete class to store your data. Not only can you then control the names when serialising, but you get compile time type safety and it's far quicker than using dynamic (every time you use dynamic a kitten dies!)
So I would create two classes like this:
//Root class so you don't need to serialise an anonymous type and can easily deserialise later
public class Root
{
public List<Document> Documents { get; set; }
}
public class Document
{
public string Index { get; set; }
public string Type { get; set; }
public string Id { get; set; }
public string Title { get; set; }
//This attribute controls the JSON property name
[JsonProperty("#Timestamp")]
public string Timestamp { get; set; }
}
And serialise something like this:
var root = new Documents();
root.Documents = new List<Document>
{
new Document
{
Index = ""index-name-test",
Type = "doc",
Id = g.ToString(),
Title = "title1",
Timestamp = DateTime.UtcNow
}
};
var json = JsonConvert.SerializeObject(root);

Related

Deserializing array of JSONElement back to a concrete list not working when using System.Text.Json

I have a problem when deserializing an object. The object has a property (data) that is a list of JSONElement. I'm doing:
using var doc = JsonDocument.Parse(JsonSerializer.Serialize(result));
var e = doc.RootElement.GetProperty("data");
var data = JsonSerializer.Deserialize<List<MyItem>>(e);
The serialized result variable has the following content:
{
"data":[
{
"id":245,
"number":14,
"name":"Test"
}
],
"totalCount":-1,
"groupCount":-1,
"summary":null
}
And the MyItem class is as follows:
public class MyItem
{
public int Id { get; set; }
public int Number { get; set; }
public string Name { get; set; }
}
The data variable is a list with x items. However all items are empty instances.
What am I doing wrong?
The problem is likely that your data is using lowercase property names which are not translated to the property names in your class with the default deserialization settings.
using System.Text.Json;
dynamic result = new
{
data = new dynamic[] {
new {
id = 245,
number = 14,
name = "Test"
}
},
totalCount = -1,
groupCount = -1
};
using var doc = JsonDocument.Parse(JsonSerializer.Serialize(result));
var e = doc.RootElement.GetProperty("data");
List<MyItem> data = JsonSerializer.Deserialize<List<MyItem>>(e);
Console.WriteLine($"{data.First().Id} {data.First().Number} {data.First().Name}");
The above code won't work with your MyItem class, but try this instead:
public class MyItem
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("number")]
public int Number { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
If it works, either use the JsonPropertyName on all your properties or else consider changing your deserialization options.
Everythig can be done in one string of code. You just need to set PropertyNameCaseInsensitive = true of JsonSerializerOptions. Better to make it in a startup file.
List<MyItem> data = JsonDocument.Parse(json).RootElement.GetProperty("data")
.Deserialize<List<MyItem>>();

How to search polymorphic JSON objects in c#?

Deserializing the following json
{
"MetaData1": "hello world",
"MetaData2": 2022,
"Data": {
"ObjectA": {
"id": 1,
"name": "steve",
"hobbies": 1
},
"ObjectB": {
"id": 2,
"name": "dave",
"age": 55
}
}
}
into corresponding c# objects
public class ObjectBase
{
public int id { get; set; }
public string name { get; set; }
}
public class ObjectA : ObjectBase
{
public int hobbies { get; set; }
}
public class ObjectB : ObjectBase
{
public int age { get; set; }
}
public class Data
{
public ObjectA ObjectA { get; set; }
public ObjectB ObjectB { get; set; }
}
public class Root
{
public string metaData1 { get; set; }
public int metaData2 { get; set; }
public Data Data { get; set; }
}
using
Root object = JsonConvert.DeserializeObject<Root>(json);
How could I search the id properties of the object properties of Root.Data for a matching int and return the corresponding name property.
It would also be useful to be able to create List<ObjectBase> so that other LINQ operations could be performed on these objects.
i think what i am ultimately after here is to end up with List<ObjectBase>.
This can be achieved easily with System.Text.Json (or Newtonsoft).
The most natural representation (IMO) given your Json structure would be to deserialize into a Dictionary<string, ObjectBase>. Then you could convert the dictionary to a List<ObjectBase>. You need a class to match the Data element in your (updated) Json:
// 'root' class to represent the 'Data' element
public class Root
{
public string MetaData1 { get; set; }
public int MetaData2 { get; set; }
public Dictionary<string, ObjectBase> Data { get; set; }
}
// Dictionary<string, ObjectBase>
var model = JsonSerializer.Deserialize<Root>(json);
foreach (var key in model.Data.Keys)
// do something with model.Data[key].id/name
// convert to List<ObjectBase>
var list = new List<ObjectBase>(model.Data.Values);
Expanding on Lasse V. Karlsen's comment, you could instead add all properties to a single class and deserialize into a Dictionary<string, SingleClass>:
public class SingleClass
{
public int id { get; set; }
public string name { get; set; }
public int hobbies { get; set; }
public int age { get; set; }
// all other properties...
}
If you choose this method you may want to consider making the additional properties nullable (if you're interested in differentiating between no hobbies property or someone with hobbies = 0, for example).
The methods above will deserialize into either ObjectBase or the SingleClass.
Demo online
Name only lookup
If you need to look up the name property based on the id then you can do that with the following code:
var semiParsed = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(json);
var name = (from node in semiParsed.Values
let id = (int)node.GetValue("id")
where id == lookupId
select (string)node.GetValue("name"))
.FirstOrDefault();
Console.WriteLine(name);
First we deserialize the json into a collection
The top most properties' name will be the keys of the Dictionary
The top most properties' object will be treated as JObjects (semi parsed jsons)
Then we perform a Linq to Json query
We iterate through the JObjects and retrieve their id property
GetValue returns a JToken and since we know it is a number, we cast it to int
We perform a filtering based on the lookupId
And we select the name property's value
Finally we need to issue a FirstOrDefault method call because the previous query returns an IEnumerable<string>
Here I have assumed that the the id is unique. If the provided lookupId is not defined inside the json then the result will be null.
Wrapping object lookup
If you need to perform a look up for the wrapping object then you need to use Json.NET Schema as well:
var generator = new JSchemaGenerator();
JSchema schemaA = generator.Generate(typeof(ObjectA));
JSchema schemaB = generator.Generate(typeof(ObjectB));
var semiParsed = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(json);
var theNode = (from node in semiParsed.Values
let id = (int)node.GetValue("id")
where id == lookupId
select node)
.FirstOrDefault();
if (theNode == null)
return;
if (theNode.IsValid(schemaA))
{
var objA = theNode.ToObject<ObjectA>();
Console.WriteLine(objA.hobbies);
} else if (theNode.IsValid(schemaB))
{
var objB = theNode.ToObject<ObjectB>();
Console.WriteLine(objB.age);
}
First we generate two json schemas from the class definitions
Then we perform almost the same query the only difference here is the select part
We return here the whole JObject object instead of just its name
Finally perform a schema validation
If the retrieved json matches to schemaA then we can safely convert (ToObject) to ObjectA
We check the json against schemaB as well
the simpliest way would be convert your json to dictionary of JObjects, in this case you don't need any classes at all
var dict = JObject.Parse(json).Properties().ToDictionary(jo => jo.Value["id"],jo=>jo.Value);
var searchId=2;
var name = dict[searchId]["name"]; // dave
or you can deserialize json to list of c# objects
List<ObjectBase> list = JObject.Parse(json).Properties()
.Select(jo => jo.Value.ToObject<ObjectBase>()).ToList();
and use linq to get data
This answer using reflection is poorly optimised as pointed out by serge in a reply to his answer.
foreach (var prop in root.GetType().GetProperties())
{
var obj = prop.GetValue(root);
if ((int) obj.GetType().GetProperty("id").GetValue(obj) == 2)
{
Console.WriteLine(obj.GetType().GetProperty("name").GetValue(obj).ToString());
break;
}
}
Another method that gets List<ObjectBase> using reflection
var objects = root.Data.Objects;
List<ObjectBase> objectList = objects.GetType().GetProperties().ToList<PropertyInfo>().ConvertAll(x => (ObjectBase)x.GetValue(objects));
I am going to leave this answer here incase it helps someone who can't get to this point by desterilisation (maybe their objects weren't crated by desterilisation).

Generate JSON object dynamically

I have a CSV which I am parsing to convert to JSON and then finally uploading to Azure blob.
This is an example of the CSV that I am reading and parsing. Each row will have its own JSON file.
humidity_sensor, sensor1, {"temp":"22.3","batt":"3.11","ss":"28","humidity":"52.6","dp":"12.144704512672"}
humidity_sensor, sensor1, {"batt":"3.14","ss":"16","humidity":"56.9","timestamp":1556568624,"temp":"21.7","dp":"12.784662018281"}
humidity_sensor, sensor1, {"pressure":"5.14","prop2":"16","current":"56.9","temp":"21.7","dp":"12.784662018281"}
This is the model I want to serialize it to:
public class SensorModel
{
[JsonProperty("sensorId")]
public string SensorId { get; set; }
[JsonProperty("Inbound_data")]
public Inbound Inbounddata { get; set; }
[JsonProperty("ts")]
public DateTime Ts { get; set; }
}
public class Inbound
{
}
So the output is of the following format:
{
"sensorId":"sensor1",
"data_in":{
},
"ts":"2020-02-11T18:07:29Z"
}
The value in Inbound is the JSON from the CSV which is not constant and will change with each row of the CSV.
SensorModel sensorModel = new SensorModel
{
SensorId = sensorId,
Ts = utcTimestamp,
Inbounddata = new Inbound
{
}
};
But since I am not certain what is going to be in that node I can't define the properties in the Inbound class.
I tried using dynamic like this:
dynamic data = JObject.Parse(values[r, 4].ToString());
The right hand side of this expression is the value from CSV.
How can I dynamically figure out what properties are required under the inbound node. I could have simply updated the model to set the inbound property as JObject and then while creating the model assigned value to it but I need all the values of the inbound node to translate by looking up in the database.
Is there any way to achieve this?
You could declare Inbounddata as Dictionary<string,string>
public class SensorModel
{
[JsonProperty("sensorId")]
public string SensorId { get; set; }
[JsonProperty("data_in")]
public Dictionary<string,string> Inbounddata { get; set; }
[JsonProperty("ts")]
public DateTime Ts { get; set; }
}
For example,
var sensorModel = new SensorModel
{
SensorId = "2",
Ts = DateTime.Now,
Inbounddata = new Dictionary<string,string>
{
["temp"] = "22.5",
["batt"] = "3.11",
["ss"] = "22"
}
};
var result = JsonConvert.SerializeObject(sensorModel);
Output
{"sensorId":"2","data_in":{"temp":"22.5","batt":"3.11","ss":"22"},"ts":"2020-02-24T20:46:39.9728582+05:30"}

deserialize json in C# with illegal variable characters

I'm writing a .NET library (using Newtonsoft) that interacts with a REST service, and I have a service that's returning json, but the json id field is called '$id'. So I can't just create a corresponding $id property in my C# data class. I tried to use something like
[JsonObject(MemberSerialization.OptOut)]
public class DocData
{
[JsonProperty("$id")]
public string id { get; set; }
public string Name { get; set; }
}
but while Name gets assigned, the id property does not. Anyone know how to map this json key to .NET?
Thanks
It looks like this is a bug in JSON.NET, and I think you should report it. It works fine in LINQPad for any property name except $id.
void Main()
{
var s = JsonConvert.SerializeObject(new DocData{id = "hi", Name = "world"}).Dump();
JsonConvert.DeserializeObject<DocData>(s).Dump();
}
public class DocData
{
// [JsonProperty("i$d")] // this would work
// [JsonProperty("_id")] // as would this
// [JsonProperty("$name")] // and even this
[JsonProperty("$id")] // but this fails
public string id { get; set; }
public string Name { get; set; }
}
Evidently Json.NET uses $id as a reserved word to help it deal with object references.
var dd = new DocData{id = "hi", Name = "world"};
JsonConvert.SerializeObject(new[]{dd, dd}, new JsonSerializerSettings{PreserveReferencesHandling = PreserveReferencesHandling.Objects}).Dump();
// Output: [{"$id":"1","$id":"hi","Name":"world"},{"$ref":"1"}]
Still, it seems like it should let you capture the $id property if you're not using reference handling.
As a workaround, you can parse it into a JObject, and pull the property out directly:
var id = JObject.Parse(s)["$id"];
var obj = JsonConvert.DeserializeObject<DocData>(s);
obj.id = id;

Deserialise JSON with random key

I am accessing an API which is returning JSon in the format:
{"status":1,"complete":1,"list":{"293352541":{"item_id":"293352541","fave":"0"},"247320106":{"item_id":"247320106","fave":"0"},"291842735":{"item_id":"291842735","fave":"0"} .....
The problem I am having is with the number before the item_id tag. It is breaking any attempt I make at deserialising as I cannot represent this random integer in an object that I deserialise in to.
I would expect this number to be, for example, the word "Item", so that it is key representing the enclosed object, but having this number means I cannot make an object representation of the JSon.
So
public class MyClass
{
public string status { get; set; }
public string complete { get; set; }
public List<MyObject> list { get; set; }
}
public class MyObject
{
public string item_id { get; set; }
public string fave { get; set; }
}
then
var items = new JavaScriptSerializer().Deserialize<MyClass>(jsontext);
dersialises, but items.list is empty.
Also,
dynamic result = JSon.Parse(jsontext);
works, but I cannot deserialise or access the list of items in a nice way.
Is there any way to do this? thanks
Because it doesn't require predefined types to deserialize into, you can do this with json.net (also available with nuget). For instance:
var jObj = JObject.Parse(data);
var sense = jObj["list"]
.Select(x => (JProperty)x)
.Select(p => new {
propName = p.Name,
itemId = p.Value["item_id"],
fave = p.Value["fave"]});

Categories