I have two enums:
enum myEnum
{
item_one = 1,
item_two = 2,
}
enum myEnum2
{
item_four = 4,
item_five = 5,
}
I would like to represent these as Json objects so that I can send then off when a http request is made. the goal is to have them look like:
{
myEnum:{[
{
"code": 1, "definition": "item_one"
},
{
"code": 2, "definition": "item_two"
}
]},
myEnum2:{[
{
"code": 4, "definition": "item_four"
},
{
"code": 5, "definition": "item_five"
}
]},
}
I would create an in-between mapping object, that can be put through a serializer like Newtonsoft.Json or System.Text.Json:
// Out mapping object
class EnumMapper {
public int Code { get; set; }
public string Definition { get; set; }
}
// Create the target output format
var result = new Dictionary<string, List<EnumMapper>>();
// Go over an enum and add it to the dictionary
// Should properly be made into a method so it easier to add more enums
foreach(var enumValue in Enum.GetValue(typeof(myEnum))) {
// List containing all values of a single enum
var enumValues = new List<EnumMapper>();
enumValues.Add(new EnumMapper {
Code = (int)enumValue,
Description = Enum.GetName(enumValue)
});
// Add the enum values to the output dictionary
result.Add("myEnum", enumValues)
}
// Serialize to JSON
var json = JsonConvert.Serialize(result)
I haven't tested the above code - but you should be able to grasp the general idea from it.
Related
We have a project which using System.Text.Json in .NET 5 instead of Newtonsoft JObject. Using Newtonsoft, it is pretty easy to replace dynamic JSON data e.g. as shown below:
siteDataObject["student"] = JArray.FromObject(studentservice.GetStudents());
When studentservice.GetStudents() is return List as below structure
internal class Student {
public int Id { get; set; }
public string Name { get; set; }
public string ContactPhone { get; set; }
public IEnumerable<MedicalRecord> MedicalRecords { get; set; }
}
internal class MedicalRecord {
public int Id { get; set; }
public string Name { get; set; }
public DateTime RecordDate { get; set; }
public IEnumerable<DiseaseLog> DiseaseLogs{ get; set; }
}
internal class DiseaseLog {
public int Id { get; set; }
public string Name { get; set; }
public DateTime LogDate { get; set; }
}
but in System.Text.Json
foreach (var element in doc.RootElement.EnumerateObject()) {
if (element.Name == "student") {
writer.WritePropertyName(element.Name);
}
else {
element.WriteTo(writer);
}
}
I don't know how to convert List<student> into JSON array data, when student class have many properties with multi collection inside.
Can anyone advise how to convert it ?
To clarify, I need to propose the full code for this, I have a dynamic json string and want to replace element : students into new record, the code will be
var dynamicJson = #"{'roomid':1,'roomcode':'Code001','students':[1],'contentdata':'say hello','footerdata':'cookie policy'}";
using MemoryStream stream = new MemoryStream();
using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
using var dynamicDocument = JsonDocument.Parse(dynamicJson);
writer.WriteStartObject();
foreach (var element in dynamicDocument.RootElement.EnumerateObject())
{
if (element.Name == "students")
{
// unknown how to modify the student record into array
}
else
element.WriteTo(writer);
}
writer.WriteEndObject();
stream.Flush();
var modifyJson = Encoding.UTF8.GetString(stream.ToArray());
I know how to modify student value , if student element is string, but I don't know how to modify it into array, by using simple code. As student have multi class inside.
My expected result should be
{
"roomid": 1,
"roomcode": "Code001",
"students": [
{
"id": 1,
"Name": "Wilson",
"ContactPhone": "123-122-3311",
"MedicalRecords": [
{
"id": 101,
"Name ": "Medial record 101011",
"RecordDate": "2021-12-31",
"DiseaseLogs": [
{
"id": 18211,
"Name ": "Patient Log 19292",
"LogDate": "2020-1-31"
},
{
"id": 18212,
"Name ": "Patient Log 2911w",
"LogDate": "2020-3-31"
}
]
}
]
}
],
"contentdata": "say hello",
"footerdata": "cookie policy"
}
In .NET 5 there is no modifiable JSON Document Object Model built into to System.Text.Json. JsonDocument is read-only, and System.Text.Json.Nodes was only introduced in .NET 6. Thus, the easiest way to deserialize, modify and re-serialize free-form JSON in .NET 5 is to deserialize to some partial data model, with unknown values bound into a dictionary.
If you do not care about the order of properties at the root level, you could deserialize to a model with a public object students { get; set; } property, and bind the remaining elements to a JsonExtensionData overflow dictionary:
public class RootObject
{
public object students { get; set; }
[System.Text.Json.Serialization.JsonExtensionDataAttribute]
public IDictionary<string, object> ExtensionData { get; set; }
}
Then deserialize, modify and re-serialize as follows:
var students = new List<Student> { /* Initialize these as required... */ };
var dynamicJson = #"{""roomid"":1,""roomcode"":""Code001"",""students"":[1],""contentdata"":""say hello"",""footerdata"":""cookie policy""}";
var root = JsonSerializer.Deserialize<RootObject>(dynamicJson);
root.students = students;
var modifyJson = JsonSerializer.Serialize(root, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true });
Which results in
{
"students": [
{
"id": 1,
"name": "Wilson",
"contactPhone": "123-122-3311",
"medicalRecords": [
{
"id": 101,
"name": "Medial record 101011",
"recordDate": "2021-12-31T00:00:00",
"diseaseLogs": [
{
"id": 18211,
"name": "Patient Log 19292",
"logDate": "2020-01-31T00:00:00"
},
{
"id": 18212,
"name": "Patient Log 2911w",
"logDate": "2020-03-31T00:00:00"
}
]
}
]
}
],
"roomid": 1,
"roomcode": "Code001",
"contentdata": "say hello",
"footerdata": "cookie policy"
}
the students property must be declared as object because the input JSON already has an array containing a single integer value; declaring it as public List<Student> students { get; set; } would result in a deserialization when initially loading the JSON.
Demo fiddle #1 here.
If you do care about the order of properties at the root level, you could deserialize to an OrderedDictionary (an old order-preserving non-generic dictionary dating from .NET Framework 2.0 which is still around and supported), overwrite the "students" value, and re-serialize:
var students = new List<Student> { /* Initialize these as required... */ };
var dynamicJson = #"{""roomid"":1,""roomcode"":""Code001"",""students"":[1],""contentdata"":""say hello"",""footerdata"":""cookie policy""}";
var root = JsonSerializer.Deserialize<OrderedDictionary>(dynamicJson);
root["students"] = students;
var modifyJson = JsonSerializer.Serialize(root, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true });
Which results in
{
"roomid": 1,
"roomcode": "Code001",
"students": [
{
"id": 1,
"name": "Wilson",
"contactPhone": "123-122-3311",
"medicalRecords": [
{
"id": 101,
"name": "Medial record 101011",
"recordDate": "2021-12-31T00:00:00",
"diseaseLogs": [
{
"id": 18211,
"name": "Patient Log 19292",
"logDate": "2020-01-31T00:00:00"
},
{
"id": 18212,
"name": "Patient Log 2911w",
"logDate": "2020-03-31T00:00:00"
}
]
}
]
}
],
"contentdata": "say hello",
"footerdata": "cookie policy"
}
Demo fiddle #2 here.
In .NET 6 this all becomes easier through use of the System.Text.Json.Nodes editable JSON Document Object Model:
var dynamicJson = #"{""roomid"":1,""roomcode"":""Code001"",""students"":[1],""contentdata"":""say hello"",""footerdata"":""cookie policy""}";
var nodes = JsonSerializer.Deserialize<JsonObject>(dynamicJson);
nodes["students"] = JsonSerializer.SerializeToNode(students, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
var modifyJson = nodes.ToString();
But in .NET 5 this is not possible. Demo fiddle #3 here.
I think this is what you want (array or single nested object):
var student = new Student()
{
Name = "Student",
ContactPhone = "contact",
Id = 1,
MedicalRecords = new List<MedicalRecord>()
{
new MedicalRecord()
{
Name = "Medical Record 1",
RecordDate= DateTime.Now ,
Id = 1 ,
MedicalRecords = new List<DiseaseLog>()
{
new DiseaseLog(){ Name = "some disease" ,
LogDate = DateTime.Now, Id =1 }
}
}
}
};
var data = System.Text.Json.JsonSerializer.Serialize(student);
Console.WriteLine(data);
var list = new List<Student>();
list.Add(student);
var arrayData = System.Text.Json.JsonSerializer.Serialize(list);
Console.WriteLine(arrayData);
I have the following JSON object (the shape of the object will vary hence use of dynamic):
{
"mba$maccType": [{
"class": {
"id": 1057,
"intlId": "cacc"
},
"classA": false,
"endDate": "4712-12-31T00:00:00",
"histStatus": {
"id": 5,
"intlId": "valid"
},
"objClassif": {
"id": 74,
"intlId": "mba$macc_type"
},
"secUser": {
"id": 2
},
"startDate": "2018-12-01T00:00:00",
"timestamp": "2020-01-18T07:29:21"
}
]
}
I'm using Newtonsoft.Json to parse as follows:
dynamic dyn = JObject.Parse(json);
My problem is, I'm unable to reference any dynamic properties because the parent property containing the $ gives a syntax error:
Console.WriteLine(dyn.mba$maccType);
How can I get to the value of "class.intlId" (i.e. "cacc")?
You can parse your JSON to JObject instead of dynamic type and access its items by key. Like get the first item from mba$maccType (since it's an array), then access a class token and intlId value from it
var jObject = JObject.Parse(json);
var firstItem = jObject["mba$maccType"]?.FirstOrDefault();
var result = firstItem?["class"]?["intlId"]?.ToString(); // returns "cacc"
Supposing that the only dynamic part of your JSON structure are the property names on the outer object, you can deserialize this JSON to a Dictionary<string, List<T>>. The benefit of doing this is that you would have clearly defined types for almost everything that's deserialized.
// type definitions
class ClassRecord
{
public int id { get; set; }
public string intlId { get; set; }
}
class Item
{
public ClassRecord #class { get; set; }
public bool classA { get; set; }
// etc.
}
// deserialize
var obj = JsonConvert.DeserializeObject<Dictionary<string, List<Item>>>(json);
Console.WriteLine(obj["mba$maccType"][0].#class.id); // 1057
I am struggling to build a complex JSON object this is what I want:
{"name": [ "failed","complete"], "data": ["failed":[1, 2, 3], "completed": [1, 2, 3]}
I want to convert this in a C# class as a property. There must be a connection with the failed property and the list of int. The output must be:
failed: 1, 2, 3
complete: 1,2 ,3
What is the correct syntax of a JSON object like this? And how can I declare a property of this object in c#?
I was thinking about dictionaries but maybe there is a better way?
Kind regards
I think it should be :
{"name": [ "failed","complete"], "data": {"failed":[1, 2, 3], "completed": [1, 2, 3]}}
then you can use :
public class Data {
public List<int> failed { get; set; }
public List<int> completed { get; set; }
}
public class RootObject {
public List<string> name { get; set; }
public Data data { get; set; }
}
and for any json to c#, I use :
json2csharp.com
EDIT:
I think, a better approach for your case is using dictionary and following class:
public class DataValues {
public List<int> Data;
}
and then use it like :
Dictionary<string, DataValues> x = new Dictionary<string, DataValues>();
x.Add("failed", new DataValues() {
Data = new List<int> { 1, 2, 3 }
});
x.Add("complete", new DataValues() {
Data = new List<int> { 1, 2, 3 }
});
var resultinJson = new JavaScriptSerializer().Serialize(x);
Then, the Json result is :
{
"failed": {
"Data": [1, 2, 3]
},
"complete": {
"Data": [1, 2, 3]
}
}
obviously, you can add more status or step or what ever it is called in your app. to it.
I have the following object:
{
"pickups": {
"7": [
5,
8
],
"10": [
6,
7,
9
],
"15": [
1
],
"20": [
0,
2
],
"25": [
3,
4
]
}
}
I'd like to de-serialize each pickups element into the following object:
public class Pickups {
public Pickup[] pickups;
}
public class Pickup {
public int Group; // This could be the 7, 10, 15, 20, 25, etc.
public int[] Values; // If this was the "7" grouping, it would contain 5, 8.
}
As you can see from the data its a bit tricky to do this. I've been trying to use a JsonConverter to convert the object with a bit of custom code but its been a nightmare and I haven't been able to get it right. I am wondering if anyone would know the best way to convert this type of object into the correct format I need?
While a converter would be a good choice you can still deserialize the Json and construct the desired object graph
var root = JsonConvert.DeserializeObject<RootObject>(json);
var pickups = new Pickups {
pickups = root.pickups.Select(kvp =>
new Pickup {
Group = int.Parse(kvp.Key),
Values = kvp.Value
}
).ToArray()
};
Where
public class RootObject {
public IDictionary<string, int[]> pickups { get; set; }
}
This is what son2csharp.com says, its gets error because you can not define names with starting number.
public class Pickups
{
public List<int> __invalid_name__7 { get; set; }
public List<int> __invalid_name__10 { get; set; }
public List<int> __invalid_name__15 { get; set; }
public List<int> __invalid_name__20 { get; set; }
public List<int> __invalid_name__25 { get; set; }
}
public class RootObject
{
public Pickups pickups { get; set; }
}
But I think
[DataMember(Name = "Name")]
should work cause its not an error in JSON format side.
If it is a viable option for you to use JObject.Parse(...) instead, you could use the following code (and write it more cleanly, with exception handling and safe casts and so on):
var jsonPickups = JObject.Parse(json);
var myPickups = new Pickups
{
pickups = jsonPickups.First.First.Select(x =>
{
JProperty xProp = x as JProperty;
return new Pickup
{
Group = int.Parse(xProp.Name),
Values = (xProp.Value as JArray).Select(y => int.Parse(y.ToString())).ToArray()
};
}).ToArray()
};
Note: Please post in comments if you have a better title for this post. Thanks!
I am working with an api that returns json that looks like the snippet below. I am pretty familiar with json but I have never seen a structure that looks like this.
The resultFieldList fieldNames are fixed (4 below but there are about 20). The values array is variable, but the same length for each fieldName.
I am thinking that I should count a non-null field (OrderNumber) and then iterate through each fieldName, but that seems inefficient. I am wondering if there is a better way. This is how I am getting the record count:
string result = await response.Content.ReadAsStringAsync();
JObject m_json = (JObject)JsonConvert.DeserializeObject(result);
int m_count = m_json["resultFieldList"][3]["values"].Count();
This is the json snippet.
{
"collectionName": "Transactions",
"recordCount": 0,
"skippedRecordCount": 0,
"resultFieldList":
[{
"fieldName": "SaleChannel",
"analyticsDataType": "STRING",
"values": ["Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online"]
},
{
"fieldName": "Quantity",
"analyticsDataType": "INTEGER",
"values": [1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 2, 2, 1, 3]
},
{
"fieldName": "Amount",
"analyticsDataType": "CURRENCY",
"values": ["25.00", "14.00", "14.00", "50.00", "14.00", "50.00", "14.00", "25.00", "18.00", "50.00", "36.00", "25.00", "50.00", "25.00", "14.00", "25.00", "50.00", "50.00", "25.00", "75.00"]
},
{
"fieldName": "OrderNumber",
"analyticsDataType": "STRING",
"values": ["60937129", "60937129", "53221966", "14599547", "14599547", "10478305", "10478305", "95344699", "95344699", "83413371", "65720270", "43458148", "52500933", "32742144", "32742144", "89850688", "61514108", "11080559", "90497730", "22838522"]
}]
}
The precise question is is it possible to create a new array or object where the index of each value is combined into a new object given this structure. For example, the values of index[0] are "Online",1,"25.00","60937129". So it may be a new array or a whole new json object. If so, how would I create either one or the other? The output would be something like this:
[ { "Online",1,"25.00","60937129" },{...} ]
Or
{"results": [ { "SaleChannel": "Online" , "Quantity": 1, "Amount": "25.00", "OrderNumber": "60937129" } , {...} ]
ANSWER
Using #JLRishe's answer I am able to parse and use the data for the use case - inserting api data into a data warehouse.
Edit: I believe I have addressed the main point of your question toward the end of my answer.
You're probably best off defining a type to represent this structure. Since the contents of the values arrays have varying types, you can't really make an assumption about them and would probably be safest treating them as objects:
public class MyClass
{
public string collectionName { get; set; }
public int recordCount { get; set; }
public int skippedRecordCount { get; set; }
public ResultField[] resultFieldList { get; set; }
}
public class ResultField
{
public string fieldName { get; set; }
public string analyticsDataType { get; set; }
public object[] values { get; set; }
}
Then parsing the JSON is simply a matter of:
string result = await response.Content.ReadAsStringAsync();
MyClass obj = JsonConvert.DeserializeObject<MyClass>(result);
When actually using the data, you would need to look at the value in analyticsDataType for each respective ResultField and cast the contents of the values properties accordingly.
Alternatively, if you don't care that much about treating the values as their original types, you could treat them all as strings, which would require less type checking. Everything else would remain the same:
public string[] values { get; set; }
Edit: Now that you've clarified the question a bit more, it seems this is mostly about arranging the data into a more nicely consumable fashion.
If there isn't anything outside the resultFieldList that indicates the number of values in each value array, then you would basically need to arbitrarily pick one to get the count:
int valueCount = obj.resultFieldList[0].values.Length;
Then once you have that, you could assemble the values together:
// assuming OrderItem is a class you've defined with the needed
// properties for one entry in an order
IEnumerable<OrderItem> items = Enumerable
.Range(0, valueCount)
.Select(i => new OrderItem {
SaleChannel = Convert.ToString(obj.resultFieldList[0].values[i]),
Quantity = Convert.ToInt32(obj.resultFieldList[1].values[i]),
Amount = Convert.ToDecimal(obj.resultFieldList[2].values[i]),
// etc...
});
Use Newtonsoft.Json and do something like this:
Define your classes:
public class SomeObject
{
public List<DynamicProperty> resultFieldList { get; set; }
}
public class DynamicProperty
{
public string fieldName { get; set; }
public string analyticsDataType { get; set; }
public List<object> values { get; set; }
}
Deserialize your JSON:
var obj = JsonConvert.DeserializeObject<SomeObject>(json);
var t = obj.resultFieldList[3].values.Count();
Please let me know if you have any questions. I tested using your JSON and the answer was 20. Additionally, you could use Linq to do more complex analysis.
Edited: Based on question being expanded.
Create an Order class:
public class Order
{
public string SaleChannel { get; set; }
public int Quantity { get; set; }
public Decimal Amount { get; set; }
public string OrderNumber { get; set; }
}
Generate the json enumerable based and serialize:
var jsonValue = JsonConvert.SerializeObject(Enumerable
.Range(0, obj.resultFieldList.Max(x => x.values.Count))
.Select(i => new Order()
{
SaleChannel = obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.SaleChannel))?.values[i].ToString(),
Quantity = Convert.ToInt32(obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.Quantity))?.values[i].ToString()),
Amount = Convert.ToDecimal(obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.Amount))?.values[i].ToString()),
OrderNumber = obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.OrderNumber))?.values[i].ToString()
}));