Deserialization with base class model - c#

I need to extract data from mongodb, but this data differs in coordinates, I can create a base-sub class structure and POST it to mongodb, but the coordinates do not come in the GET operation.
public class Geometry
{
public string type { get; set; }
}
public class GeoPoly:Geometry
{
public double[][][] coordinates { get; set; }
}
public class GeoMultipoly : Geometry
{
public double[][][][] coordinates { get; set; }
}
how can I do that
Should the serialization convention change and how should it change
Is the base-sub class structure suitable for this problem?
database entries:
{
"type": "Polygon",
"coordinates": [
[
[
[
51.263632,
60.962938
],
[
30.884274,
20.065517
],
[
68.832044,
14.362602
],
[
51.263632,
60.962938
]
]
]
]
},
{
"type": "MultiPolygon",
"coordinates": [
[
[
[
43.182162,
64.042209
],
[
14.721334,
22.358269
],
[
51.263632,
17.738761
],
[
43.182162,
64.042209
]
]
],
[
[
[
55.831419,
51.446822
],
[
65.66973,
20.065517
],
[
97.64424,
37.509124
],
[
55.831419,
51.446822
]
]
]
]
}

I am not sure is the best idea,
but newtonsoft json.net support serialize and deserialize with $type.
The item will be saved in DB with it's full class identifier.
You can check here
An example:
// {
// "$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests",
// "FullName": "Steve Stockholder",
// "Businesses": {
// "$type": "System.Collections.Generic.List`1[[Newtonsoft.Json.Samples.Business, Newtonsoft.Json.Tests]], mscorlib",
// "$values": [
// {
// "$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests",
// "Stars": 4,
// "Name": "Hudson Hotel"
// }
// ]
// }
// }
To use it check newtonsoft documentation here.
Example of usage:
Stockholder stockholder = new Stockholder
{
FullName = "Steve Stockholder",
Businesses = new List<Business>
{
new Hotel
{
Name = "Hudson Hotel",
Stars = 4
}
}
};
string jsonTypeNameAll = JsonConvert.SerializeObject(stockholder, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});

For this JSON right class to do is:
public class Rootobject
{
public string type { get; set; }
public float[][][][] coordinates { get; set; }
}
For GeoPoly and GeoMultipoly there practical the same. You can procesig this in two steps:
Get the list od Rootobject (List)
switch(g.type)
{
case "GeoPoly":
result.Add(new GeoPoly
{
type = g.type;
coordinates = g.coordinates
});
break;
case "GeoMultipoly":
result.Add(new GeoMultipoly
{
type = g.type;
coordinates = g.coordinates
});
break;
default:
continue;
}
At the end the result list will have a list of Geometry types in right type.

Related

Load all nested children from same collection

I have a collection with documents containing references to children from same collection.
Collection of Components:
[
{
"_id": "group01",
"type": "Group",
"children": [
{
"_id": "list01",
"order": 0
},
{
"_id": "list02",
"order": 1
}
],
"order": 0
},
{
"_id": "list01",
"type": "List",
"children": [],
"order": 0
},
{
"_id": "list02",
"type": "List",
"children": [
{
"_id": "item01",
"order": 0
}
],
"order": 0
},
{
"_id": "item01",
"type": "Item",
"children": [],
"order": 0
}
]
Model for Component:
[BsonCollection(collectionName: "Components")]
public class Component {
[JsonProperty("_id")]
[BsonElement("_id")]
[BsonId(IdGenerator = typeof (StringObjectIdGenerator))]
public string Id { get; set; }
[JsonProperty("type")]
[BsonElement("type")]
[BsonRequired]
public string Type { get; set; }
[JsonProperty("order")]
[BsonElement("order")]
public int? Order { get; set; }
[JsonProperty("children")]
[BsonElement("children")]
[BsonIgnoreIfNull]
public List <Component> Children { get; set; }
}
While loading the document, I need to load all the nested children for a given id. For example, to get document with _id: group01, I want to fetch all the properties of children nested to all levels like this:
[
{
"_id": "group01",
"type": "Group",
"children": [
{
"_id": "list01",
"type": "List",
"children": [],
"order": 0
},
{
"_id": "list02",
"type": "List",
"children": [
{
"_id": "item01",
"type": "Item",
"children": [],
"order": 0
}
],
"order": 0
}
],
"order": 0
}
]
I am able to get this with recursively populating children but this will take more time when there are more levels in the hierarchy.
public override async Task <Component> GetByIdAsync(Guid id) {
try {
var Components = (MongoCollectionBase<Component>)_dbRepository.GetCollection<Component>();
Component component = await Components.Find(i => i.Id == id.ToString()).FirstOrDefaultAsync();
for (var i = 0; i < component.Children.Count; i++) {
component.Children[i] = await GetByIdAsync(Guid.Parse(component.Children[i].Id));
}
return component;
} catch (Exception ex) {
_logger.LogException(ex, $ "Exception while reading component: {id}", id.ToString());
}
return null;
}
How to achieve this in a more optimized way with aggregation/projection? Please guide me.
In order to load the hierarchy using an aggregate with a $graphLookup, you can use the following pipeline:
var result = await Components.Aggregate()
.Match(Builders<Component>.Filter.Eq(x => x.Id, "group01"))
.GraphLookup<Component, string, string, string, Component, IEnumerable<Component>, Component>(
Components,
"children._id",
"_id",
"$children._id",
"children",
"depth")
.ToListAsync();
The result contains the requested parent with all children and is retrieved from the database in a single request.
For this to work, I adjusted the model:
Removed BsonRequired from Type
Added Depth property
public class Component
{
[JsonProperty("_id")]
[BsonElement("_id")]
public string Id { get; set; }
[JsonProperty("type")]
[BsonElement("type")]
public string Type { get; set; }
[JsonProperty("order")]
[BsonElement("order")]
public int? Order { get; set; }
[JsonProperty("children")]
[BsonElement("children")]
[BsonIgnoreIfNull]
public List<Component> Children { get; set; }
[BsonElement("depth")]
public int Depth { get; set; }
}

Deserializing specific JSON fields into a list of objects in Unity

For a personal project, I'm trying to deserialize a big Json array into a list of objects, however I repeatedly run into issues when following other guides. I have the following json file:
{
PokemonJSONList : [
{
"id": 1,
"region": "Kanto",
"name": "Bulbasaur",
"typeList": [ "Grass", "Poison" ]
},
{
"id": 2,
"region": "Kanto",
"name": "Ivysaur",
"typeList": [ "Grass", "Poison" ]
},
{
"id": 3,
"region": "Kanto",
"name": "Venusaur",
"typeList": [ "Grass", "Poison" ]
},
{
"id": 3,
"form": "f2",
"region": "Kalos",
"name": "Mega Venusaur",
"typeList": [ "Grass", "Poison" ]
},
{
"id": 3,
"form": "f3",
"region": "Galar",
"name": "G-Max Venusaur",
"typeList": [ "Grass", "Poison" ]
},
{
"id": 4,
"region": "Kanto",
"name": "Charmander",
"typeList": [ "Fire" ]
},
...
{
"id": 898,
"form": "f4",
"region": "Galar",
"Legend": "true",
"name": "Shadow Rider Calyrex",
"typeList": [ "Psychic", "Grass" ]
}
]
}
As you can see, some json entries have more key-value pairs than others, as this json file is used for more than just this project. I only need the Name, Region, Typelist, and Legend(not shown, as the json file goes on considerably longer) fields. I also have this class:
[System.Serializable]
public class PokemonList : MonoBehaviour
{
public List<PokemonOBJ> PokemonJSONList;
}
Meant to recieve this json data, and this class:
[System.Serializable]
public class PokemonOBJ : MonoBehaviour
{
public string Name { get; set; }
public string Region { get; set; }
public List<string> TypeList { get; set; }
public bool Legend { get; set; }
}
To be the base object for storing the data for each entry. However, when trying to serialize the json file as such:
string jsonString = #"Assets/JSON/data.json";
public PokemonList PokemonDB;
PokemonDB = JsonUtility.FromJson<PokemonList>(jsonString);
results in a "cannot deserialize JSON to new instances of type PokemonList"
tutorials I used showed using PokemonOBJ for FromJson but that results an error in vs itself, and I'm a bit lost here on how to get this working properly, and what I'm doing wrong.
From the docs:
Only plain classes and structures are supported; classes derived from
UnityEngine.Object (such as MonoBehaviour or ScriptableObject) are
not.

C#/Linq - Group by the 2nd item returned in string array whilst keep parent

I have the following data returned from an API and I am trying to use linq to re-shape the data.
Is it actually possible to do this and get the below expected result.
"results": [
{
"Description": "Describe1",
"Cost": 5.00,
"Category": [
"Online",
"Games"
]
},
{
"Description": "Describe2",
"Cost": 4.00,
"Category": [
"Online",
"Games"
]
},
{
"Description": "Describe3",
"Cost": 3.00,
"Category": [
"Online",
"Grocery"
]
},
{
"Description": "Describe4",
"Cost": 3.00,
"Category": [
"Transport",
"Bus"
]
},
{
"Description": "Describe5",
"Cost": 3.00,
"Category": [
"Transport",
"Bus"
]
},
{
"Description": "Describe5",
"Cost": 10.00,
"Category": [
"Transport",
"Train"
]
}
}
The Final output I am trying to achieve from the above:
{ name : "Online",
data: [
{
name: Games,
value: 9.00
},
{
name : Grocery,
value: 3.00
}],
name : "Transport",
data: [
{
name: Bus,
value: 6.00
},
{
name : Train,
value: 10.00
}],
}
Grouping by FirstOrDefault is all easy enough for first level stats, but I can't see where to start for the sub-grouping!
Thanks in advance.
This should work:
class ResultItem {
public string Name { get; set; }
public double Value { get; set; }
}
class ResultGroup {
public string Name { get; set; }
public ResultItem[] Data { get; set; }
}
var results = items
.GroupBy(x => x.Category[0])
.Select(g => new ResultGroup {
Name = g.Key,
Data = g
.GroupBy(x => x.Category[1])
.Select(g2 => new ResultItem { Name = g2.Key, Value = g2.Sum(x => x.Cost) })
.ToArray()
});
Answering your question in two parts,
Part 1
The first part would consists of few steps
Deserialize existing Json to Custom Data Structures
Modify and create new data structures
The above steps could be achieved with Linq as following.
var parsedData = JsonConvert.DeserializeObject<RootObject>(str).Results.GroupBy(x=> x.Category.First())
.Select(x=>
new {
name= x.Key,
 data =  x.GroupBy(c=>c.Category.Last())
.Select(c=> new {value=c.Sum(f=>f.Cost),name=c.Key})
});
Where RootObject is defined as
public class Result
{
    public string Description { get; set; }
    public double Cost { get; set; }
    public List<string> Category { get; set; }
}
public class RootObject
{
[JsonProperty("results")]
    public List<Result> Results { get; set; }
}
Part 2
The second part has to be executed depended how you want the Json to be formated. If you observe the expected result given in OP, that doesn't look like a valid Json. Simplifying it for representational purposes, the Json string looks like following
{
name : "Online",
data: [....],
name : "Transport",
data: [...],
}
As observed, the name and data fields are duplicated within json. If you would like to have result in this particular manner, you need to Serialize the Json recieved in Part 1, and modify it using string manipulation. For example,
var jsonCollection = parsedData.Select(x=> Regex.Replace(JsonConvert.SerializeObject(x,Newtonsoft.Json.Formatting.Indented),#"^{|}$",string.Empty));
var finalResult = $"{{{string.Join(",",jsonCollection)}}}";
Output
{
"name": "Online",
"data": [
{
"value": 9.0,
"name": "Games"
},
{
"value": 3.0,
"name": "Grocery"
}
]
,
"name": "Transport",
"data": [
{
"value": 6.0,
"name": "Bus"
},
{
"value": 10.0,
"name": "Train"
}
]
}
If your desired result needs to be a valid json, then you could ensure it is an array. For example,
[
{
name : "Online",
data: [....]
},
{
name : "Transport",
data: [...],
}
]
Then you could serialize the result you got in Part 1 directly.
var finalResult = JsonConvert.SerializeObject(parsedData, Newtonsoft.Json.Formatting.Indented);
Output
[
{
"name": "Online",
"data": [
{
"value": 9.0,
"name": "Games"
},
{
"value": 3.0,
"name": "Grocery"
}
]
},
{
"name": "Transport",
"data": [
{
"value": 6.0,
"name": "Bus"
},
{
"value": 10.0,
"name": "Train"
}
]
}
]
Demo Code
You can achieve it in this simple way, Live demo here
var parsedJsonObject = JsonConvert.DeserializeObject<List<ObjectName>>(jsonObject);
var normalizedData = parsedJsonObject.SelectMany(pParent => pParent.Category, (pParent, pCategory) =>
new { pParent, pCategory }).Select(ParentAndCategory =>
new
{
Cost = ParentAndCategory.pParent.Cost,
Category = ParentAndCategory.pCategory,
}).ToList();
var aggregatedData = new List<ObjectName2>();
for(int i = 0; i < (normalizedData.Count - 1);)
{
aggregatedData.Add(new ObjectName2{ Cost = normalizedData[i].Cost, Category1 = normalizedData[i].Category, Category2 = normalizedData[i + 1].Category });
i += 2;
}
var result = aggregatedData.GroupBy(p => p.Category1)
.Select(g => new
{
name = g.Key,
data = g.GroupBy(p => p.Category2).Select(g2 =>
new { name = g2.Key, value = g2.Sum(p2 => p2.Cost) })
}).ToList();
foreach(var item in result)
Console.WriteLine(JsonConvert.SerializeObject(item));
Output
{"name":"Online","data":[{"name":"Games","value":9.00},{"name":"Grocery","value":3.00}]}
{"name":"Transport","data":[{"name":"Bus","value":6.00},{"name":"Train","value":10.00}]}

Normalise JSON for flexible WebAPI response

I have a WebAPI method that returns Json in a flexible structure that depends on the request.
Part of the problem is that there could be any number of columns, and they could be any type. The 2 given below (Code and Count) are just one example.
This structure is based on the underlying classes but there could be any number of columns in the output. So, rather than the usual properties you might expect, these are objects in a collection with Name and Value properties.
The downside of this flexible approach is that it gives a non-standard format.
Is there a way to transform this into a more normalised shape? Are there maybe some attributes I can add to the class properties to change the way they are serialised?
For example, where there are 2 columns - Code (string) and Count (numeric):
Current Json:
{
"Rows": [
{
"Columns": [
{
"Value": "1",
"Name": "Code"
},
{
"Value": 13,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "2",
"Name": "Code"
},
{
"Value": 12,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "9",
"Name": "Code"
},
{
"Value": 1,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "5",
"Name": "Code"
},
{
"Value": 2,
"Name": "Count"
}
]
}
]
}
Ideally I'd like to transform it to this:
{
"Rows": [
{
"Code": "1",
"Count": 13
},
{
"Code": "2",
"Count": 12
},
{
"Code": "9",
"Count": 1
},
{
"Code": "5",
"Count": 2
}
]
}
The controller method (C#)
public ReportResponse Get(ReportRequest request)
{
var result = ReportLogic.GetReport(request);
return result;
}
The output classes
public class ReportResponse
{
public List<ReportRow> Rows { get; set; }
public ReportResponse()
{
Rows = new List<ReportRow>();
}
}
public class ReportRow
{
public List<ReportColumn> Columns { get; set; }
public ReportRow()
{
Columns = new List<ReportColumn>();
}
}
public class ReportColumn<T> : ReportColumn
{
public T Value { get; set; }
public ReportColumn(string name)
{
Name = name;
}
}
public abstract class ReportColumn
{
public string Name { get; internal set; }
}
I think the easiest way would be to map your class to a dictionary before serializing. Something like:
var dictionaries = List<Dictionary<string, object>();
foreach(var column in rows.Columns)
{
dictionaries.Add(new Dictionary<string, object>{{column.Name, column.Value}});
}
Then serialize the dictionaries variable should do the trick.
If you're using the output in JavaScript, you could translate as follows:
var
data = {
"Rows": [
{
"Columns": [
{
"Value": "1",
"Name": "Code"
},
{
"Value": 13,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "2",
"Name": "Code"
},
{
"Value": 12,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "9",
"Name": "Code"
},
{
"Value": 1,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "5",
"Name": "Code"
},
{
"Value": 2,
"Name": "Count"
}
]
}
]
},
output = [
];
data.Rows.forEach(function (row)
{
var
newRow = {};
row.Columns.forEach(function (column)
{
newRow[column.Name] = column.Value;
});
output.push(newRow);
})
console.log(JSON.stringify(output));

Google Play Developer Api and JsonConvert.DeserializeObject

I am trying to deserialize an AchievementConfigurations: list REST Response from here https://developers.google.com/games/services/publishing/api/achievementConfigurations/list.
The problem is that this line only fills out the top level object and the List remains empty. No error messages are throw which makes this difficult to track down what is going on. I used a website to generate the json structured classes and after that I removed the duplicates which where unnecessary.
The response looks like this, I have removed the achievementConfiguration resource because it is really long but it can be found here https://developers.google.com/games/services/publishing/api/achievementConfigurations#resource
{
"kind": "gamesConfiguration#achievementConfigurationListResponse",
"nextPageToken": string,
"items": [
achievementConfigurations Resource
]
}
I have a series of classes I have created mirroring the data starting with the AchievementConfigurationListResponse class
public class AchievementConfigurationListResponse
{
public string kind = "gamesConfiguration#achievementConfigurationListResponse";
public string nextPageToken = "";
List<AchievementConfigurationResource> items = new List<AchievementConfigurationResource>();
}
Next up is the AchievementConfigurationResource which is an item in the list, it has several nested objects
public class AchievementConfigurationResource
{
public static string[] types = new string[] { "STANDARD", "INCREMENTAL" };
public static string[] states = new string[] { "REVEALED", "HIDDEN", "UNLOCKED" };
public string kind = "gamesConfiguration#achievementConfiguration";
public string token = "";
public string id = "";
public string achievementType = types[0];
public string initialState = states[0];
public int? stepsToUnlock;
public AchievementConfigurationDetail draft = new AchievementDataResource();
public AchievementConfigurationDetail published = new AchievementDataResource();
}
Those nested object are of this type of AchievementConfigurationDetail
public class AchievementConfigurationDetail
{
public string kind = "gamesConfiguration#achievementConfigurationDetail";
public LocalizedStringBundle name = new LocalizedStringBundle();
public LocalizedStringBundle description = new LocalizedStringBundle();
public int pointValue = 5;
public string iconUrl = "";
public int sortRank = 1;
}
Which contains several LocalizedStringBundles
public class LocalizedStringBundle
{
public string kind = "gamesConfiguration#localizedStringBundle";
public List<Translation> translations = new List<Translation>();
public class Translation
{
public string kind = "gamesConfiguration#localizedString";
public string locale = "en-US";
public string value = "";
}
}
I call this on the json with the following line:
AchievementConfigurationListResponse res = JsonConvert.DeserializeObject<AchievementConfigurationListResponse>(content);
Here is a copy of the response, sensitive data removed but the keys and structure are all intact. This one only contains a single record because the full file is something like 5000 lines long.
{
"kind": "gamesConfiguration#achievementConfigurationListResponse",
"items": [
{
"kind": "gamesConfiguration#achievementConfiguration",
"token": "Unique Token",
"id": "Unique ID",
"achievementType": "STANDARD",
"initialState": "REVEALED",
"draft": {
"kind": "gamesConfiguration#achievementConfigurationDetail",
"name": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Name"
}
]
},
"description": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Description"
}
]
},
"pointValue": 5,
"iconUrl": "Icon url",
"sortRank": 1
},
"published": {
"kind": "gamesConfiguration#achievementConfigurationDetail",
"name": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Name"
}
]
},
"description": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Description"
}
]
},
"pointValue": 5,
"iconUrl": "Icon url",
"sortRank": 1
}
}
]
}
Json.NET does not serialize private members by default. Thus you need to make AchievementConfigurationListResponse.items be public:
public List<AchievementConfigurationResource> items = new List<AchievementConfigurationResource>();
Alternatively, mark it with [JsonProperty] which enables serialization of private members:
[JsonProperty]
List<AchievementConfigurationResource> items = new List<AchievementConfigurationResource>();

Categories

Resources