Serializing "string list" to JSON in C# - c#

(I'v restated my question here: Creating class instances based on dynamic item lists)
I'm currently working on a program in Visual Studio 2015 with C#.
I have 5 list strings that contain data that I wish to serialize to a json file.
public List<string> name { get; private set; }
public List<string> userImageURL { get; private set; }
public List<string> nickname { get; private set; }
public List<string> info { get; private set; }
public List<string> available { get; private set; }
An example of the desired json file format is the fallowing:
{
"users" :
[
{
"name" : "name1",
"userImageURL" : "userImageURL1",
"nickname" : "nickname1",
"info" : "info1",
"available" : false,
},
{
"name" : "name2",
"userImageURL" : "userImageURL2",
"nickname" : "nickname2",
"info" : "info2",
"available" : false,
},
{
"name" : "name3",
"userImageURL" : "userImageURL3",
"nickname" : "nickname3",
"info" : "info3",
"available" : false,
},
{
"name" : "name4",
"userImageURL" : "userImageURL4",
"nickname" : "nickname4",
"info" : "info4",
"available" : false,
}
]
}
Note that there might be errors in the json example above.
I've tried combining the 5 lists to create 1 list to serialize it using the following code:
users = new List<string>(name.Count + userImageURL.Count + nickname.Count + info.Count + available.Count);
allPlayers.AddRange(name);
allPlayers.AddRange(userImageURL);
allPlayers.AddRange(nickname);
allPlayers.AddRange(info);
allPlayers.AddRange(available);
Then I serialize the list with the fallowing code:
string data = JsonConvert.SerializeObject(users);
File.WriteAllText("data.json", data);
This just creates an array of unorganized objects. I wish to know how can I organize them as expressed in the format above.
PS: I'm pretty new to coding as you can tell. Sorry if I'm not expressing the question correctly or using the right terminology. Also, this is not the original code. The code creates this lists which I wish to serialize into a json file.
PSS: This data is collected using HtmlAgilityPack. I asked a question yesterday asking how could I parse an html file and serialize it's data to a json file. Using HtmlAgilityPack to get specific data in C# and serialize it to json . As nobody answered, I decided to try and do it myself. The method that I used may not be the best, but it is what I could do with the knowledge that I have.

I would suggest refactoring your code to start with - instead of having 5 "parallel collections", have a single collection of a new type, User:
public class User
{
public string Name { get; set; }
public string ImageUrl { get; set; }
public string NickName { get; set; }
public string Info { get; set; }
public bool Available { get; set; }
}
...
// In your containing type
public List<User> Users { get; set; }
This is likely to make life simpler not just for your JSON, but for the rest of the code too - because you no longer have the possibility of having more nicknames than image URLs, etc. In general, having multiple collections that must be kept in sync with each other is an antipattern. There are times where it's appropriate - typically providing different efficient ways of retrieving the same data - but for something like this it's best avoided.

It is actually what Jon says, but to move from your parallel lists to Jon's single list you need something like (assuming all lists have the same number of elements in the same order):
Users = new List<User>();
for (var i = 0; i < name.Count; i++)
{
Users.Add(new User
{
Available = available[i],
ImageUrl = userImageURL[i],
Info = info[i],
Name = name[i],
NickName = nickname[i]
});
}
And then serialise the Users list.

Related

How to access and store a particular object from a json file in c#

I am trying to make a multi-subject quiz game for children using unity3D and questions and answers are stored in a json file. Now i want to create an object named "science" and "maths" and want to store their respective questions in them and when i want to access science i could loop and find and just store the science question in my string instead of reading the whole json file.
here is my json file.
Science ={
"CourseName":"Science",
"No_Of_Ques":4,
"Ques_Data":[
{ "Quesion":"which is the biggest planet in the solar system?",
"Answer":"jupiter",
"options":["mars","earth","venus","jupiter"]
},
{ "Quesion":"How many planets are there in solar system?",
"Answer":"Eight",
"options":["Seven","Nine","Five","Eight"]
},
{ "Quesion":"which is the closest planet to the sun?",
"Answer":"mercury",
"options":["mars","saturn","venus","mercury"]
},
{ "Quesion":"How many moons does jupiter have?",
"Answer":"12",
"options":["5","13","9","12"]
}
]
}
and this is how i have been acessing it so far
path = Application.dataPath + "/QnA.json";
string json = File.ReadAllText(path);
Course c1 = JsonUtility.FromJson<Course>(json);
return c1;
Course and needed serializable Classes:
[Serializable] public class Course
{
public string CourseName;
public string No_Of_Ques;
public QnA[] Ques_Data;
}
[Serializable]
public class QnA
{
public string Quesion;
public string Answer;
public string[] options;
}
i have tried so many things like Deserialization and Jobject asset but none of them seem to work and every implementation that i have found on the internet has the json data in the same file as the c# code but i can not do that as my json contains hundreds of lines of data. kindly help me out a little.
Create a course class in which create getter and setter functions for all of your json keys, for example:
if your json file is like that:
[
{
"CourseName": "Science",
"No_Of_Ques": 1,
...
},
{
"CourseName": "Math",
"No_Of_Ques": 1,
...
}
]
then course class should be:
public class Course
{
public string CourseName { get; set; }
public int No_Of_Ques { get; set; }
}
In your main class or anywhere you can access your selected course, here i am using only 0 index of a json, you can also loop through whole json and find your desirable course.
StreamReader to read a file
convert it to json
Deserialize the json as per your course
Console it
using (StreamReader r = new StreamReader("../../../js.json"))
{
string json = r.ReadToEnd();
List<Course> ro = JsonSerializer.Deserialize<List<Course>>(json);
Console.WriteLine(ro[0].CourseName);
}
I added json file in the same dire where my mainClass file is, as StreamReader requires an absolute path therefore I used an absoulte path for my json file.
using (StreamReader r = new StreamReader("../../../js.json"))
Require Libs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
Note: I created a console app, not sure your app type
If you want to store multiple courses in your file you will need to store them as json array (as you do with questions):
[
{
"CourseName":"Science",
"No_Of_Ques":1,
"Ques_Data":[
{
"Question":"which is the biggest planet in the solar system?",
"Answer":"jupiter",
"options":[
"mars",
"jupiter"
]
}
]
},
{
"CourseName":"Math",
"No_Of_Ques":1,
"Ques_Data":[
{
"Question":"2 + 2",
"Answer":"4",
"options":[
"4",
"0"
]
}
]
}
]
then you can deserialize them with next structure(for example):
public class Course
{
[JsonProperty("CourseName")]
public string CourseName { get; set; }
[JsonProperty("No_Of_Ques")]
public long NoOfQues { get; set; }
[JsonProperty("Ques_Data")]
public QuesDatum[] QuesData { get; set; }
}
public class QuesDatum
{
[JsonProperty("Question")]
public string Question{ get; set; }
[JsonProperty("Answer")]
public string Answer { get; set; }
[JsonProperty("options")]
public string[] Options { get; set; }
}
var courses = JsonConvert.DeserializeObject<List<Course>>(jsonString);
var course = courses.Where(...).FirstOrDefault();
Or try to use json path:
var course = JToken.Parse(jsonString)
.SelectToken("$[?(#.CourseName == 'Math')]")
.ToObject<Course>();
As for jsonString you can obtain it in any way, reading from file for example.
P.S.
There was typo "Quesion" -> "Question"
To serialize and deserialize objects you have to create a C# class (in your case should be Course class) that can be [Serializable].
First your Json should be a valid one, which it is, you can validate it here.
To serialize and deserialize you can use JsonUtility to certain point, cause it doesn't deserialize jagged arrays, complex objects etc. I recommend to use third party softwares like Newtonsoft or implement your own serialization/deserialization method for your way.
Edit:
Your JSON file should be without the "Science=" part, should look like:
{
"CourseName":"Science",
"No_Of_Ques":4,
"Ques_Data":[
{ "Quesion":"which is the biggest planet in the solar system?",
"Answer":"jupiter",
"options":["mars","earth","venus","jupiter"]
},
{ "Quesion":"How many planets are there in solar system?",
"Answer":"Eight",
"options":["Seven","Nine","Five","Eight"]
},
{ "Quesion":"which is the closest planet to the sun?",
"Answer":"mercury",
"options":["mars","saturn","venus","mercury"]
},
{ "Quesion":"How many moons does jupiter have?",
"Answer":"12",
"options":["5","13","9","12"]
}
]
}
Edit:
For your comment I think you got a misunderstood of how to handle the relation between files and variables.
You want to have (or at least it seems like this) one file for every type of course, so in this case, the text above will be your Science.json file.
When you store that information, you will do similar of what you do:
path = Application.dataPath + "/QnA.json";
string json = File.ReadAllText(path);
Course scienceCourse = JsonUtility.FromJson<Course>(json); //notice the name of the variable!
So as you can see for the variable name, you will read EVERY SINGLE JSON for every course.
The other way to do that, is to store all the courses on the same Json file, and then get them as an ARRAY of Courses -> Course[] allCourses
Using Science={...} to define your object is where you get confused about object definitions. It is not a Science object. It is a Course object.
It should be more like
{
"Courses" :
[
{
"CourseName" : "Science",
...
},
{
"CourseName" : "Maths",
...
}
]
}`.
Wrap it with:
"Quiz" :
{
"Courses" :
[
{
"CourseName" : "Science",
...
},
{
"CourseName" : "Maths",
...
}
]
}
and use
[Serializable]
public class Quiz
{
public Course[] courses;
}
To hold it as a C# object.
From here you can access your courses by quiz.Courses[0].Questions[17] or write helper methods in Quiz class to call courses by enums like quiz.GetCourse(CourseCategory.Science).Questions[8].
I also suggest using Questions instead of Question_Data. That is more object friendly and helps you semantically.
As an additional suggestion, instead of dumping all of the quiz in a single JSON, you may consider sending a single Course object depending on the course, requested using a query like http://myquizserver.com/quiz.php?course=science. Since you mentioned hundreds of lines of JSON, you may also consider getting data question by question.

Deserialize JSON into C# class containing two Dictionary properties

I'm developing an app in Xamarin and I have a very simple JSON file made of objects I would like to deserialize in one shot into the C# Class I have in my domain. (I am using the Newtonsoft Json.NET Framework)
Each JSON object has 3 properties: Title, Default and Custom. Title is a simple string, while Custom and Default are a list of pairs.
{
"messages": [
{
"Title": "...",
"Default": {
"lang1": "...",
"lang2": "..."
},
"Custom": {
"lang1": "",
"lang2": ""
}
},
{
"Title": "...",
"Default": {
"lang1": "...",
"lang2": "..."
},
"Custom": {
"lang1": "",
"lang2": ""
}
}
]
}
The C# class only contains those same three properties:
[JsonObject(MemberSerialization.OptIn)]
public class MessageItem
{
[JsonProperty]
public String Title { get; set; }
[JsonConverter(typeof (JsonDictionaryAttribute))]
public Dictionary<string, string> Default { get; set; }
[JsonConverter(typeof(JsonDictionaryAttribute))]
public Dictionary<string, string> Custom { get; set; }
}
Now, as you can see, I have already tried to specifically define the class the JsonConverter should use to deserialize the two Dictionary attributes. Problem is, when I try to deserialize the JToken containing a single "message" object I get a System.InvalidCastException.
I have tried to deserialize it both by invoking
JsonConvert.DeserializeObject<Domain.MessageItem>(messageToken.ToString());
and
messageToken.ToObject<Domain.MessageItem>();
and I'm obviously getting the same result.
By deserializing the single token, though, it works just fine, so I'm pretty sure what I'm doing wrong is something in the flag declarations I am using in the class.
I hope some of you can help me with this! Thanks in advance.
You could either deserialize to dictionary
Dictionary<string, List<MessageItem>> messageItems = JsonConvert.DeserializeObject<Dictionary<string, List<MessageItem>>>(yourJson);
foreach(MessageItem item in messageItems["messages"])
{
Console.WriteLine(item.Title);
}
OR you could write a wrapper class:
[JsonObject(MemberSerialization.OptIn)]
public class MessageItems
{
[JsonProperty("messages")]
public List<MessageItem> Messages { get; set; }
}
and deserialize to that
MessageItems messageItems = JsonConvert.DeserializeObject<MessageItems>(yourJson);
foreach(MessageItem item in messageItems.Messages)
{
Console.WriteLine(item.Title);
}
To get those examples to work I had to remove the [JsonConverter(typeof (JsonDictionaryAttribute))] attributes from your MessageItem class and replace with [JsonProperty].
You have the proper class setup for a single MessageItem, but the JSON you posted is a List<MessageItem>.
Create the following class:
public class MessageList {
[JsonProperty]
public List<MessageItem> messages { get; set; }
}
and use Newtonsoft.Json.JsonConvert.DeserializeObject<MessageList>(messageToken.ToString()) and that should do the trick.

Cosmos DB - ExecuteNextAsync returns empty objects

I have a Xamarin app and I use Cosmos DB on Azure, on the DB side I have this kind of document :
{
"_id" : ObjectId("5b84142d07bf8638bcf7a089"),
"business_id" : "5b81309a7dfa952bb4036f55",
"contentType" : "image",
"media" : "https://fimgs.net/images/perfume/375x500.30796.jpg",
"id" : "22155414-ee2b-3191-c180-3bc9d40db16b"
}
On the Xamarin side, I use the following code :
List<TempObject> TempObjects = new List<TempObject>();
Uri collectionUri = UriFactory.CreateDocumentCollectionUri(_databaseId, _collectionId);
var query = _client.CreateDocumentQuery<TempObject>(collectionUri).AsDocumentQuery();
while (query.HasMoreResults)
{
TempObjects.AddRange( await query.ExecuteNextAsync<TempObject>() );
}
Here is the data class definition :
class TempObject
{
//[JsonProperty(PropertyName = "_id")]
//public string _id { get; set; }
[JsonProperty(PropertyName = "business_id")]
public string business_id { get; set; }
[JsonProperty(PropertyName = "contentType")]
public string contentType { get; set; }
[JsonProperty(PropertyName = "media")]
public string media { get; set; }
[JsonProperty(PropertyName = "id")]
public string id { get; set; }
}
The problem is that it returns the right number of objects, but all the properties are null !
The collection has been created with the default settings and the database is almost empty, only 2 documents in this collection !
The code seems to follow exactly all the tutorials I have found !
Mainly this one : https://learn.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/cosmosdb/consuming
Also, when I use the local CosmosDB emulator with the same database, it works, but not when it is on Azure ! Does it mean that my code is correct and that the issue is on the Azure side ?
Any idea of the issue ? Or a way to debug such kind of problem ?
Thanks
Edit : I have also try with the Cosmonaut API, here is the code:
var cosmosSettings = new CosmosStoreSettings("soclozecosmosdb", "https://soclozedb.documents.azure.com:443/", "E2ipML2QWNVjWITKhX0K0pn7ooCWxbkEk0xkQIC6QIWQCmMjsLU3D2SRTLaIk0dB3bm4k4mWhlpYYpbgsrk2xw==");
ICosmosStore<TempObject> store = new CosmosStore<TempObject>(cosmosSettings);
var users = store.Query().ToList();
But, the query returns 0 objects !
A similar problem can occur if, for whatever reason, you mistakenly subclass your data object as a Document. No reason to do that with the CosmosDB SDK.

C# MongoDB update object from oplog

What I have is a MongoDB which contains a collection of products. Lets say each product has the following structure:
public ObjectId Id { get; set; }
public string label { get; set; }
public List<IngredientsModel> ingredients { get; set; }
Now my C# program gets notified via the oplog if there was a change. This oplog looks like this:
{
"ts" : Timestamp(1425392023, 1),
"h" : NumberLong("-7452324865462273810"),
"v" : 2,
"op" : "u",
"ns" : "coll.products",
"o2" : { "_id" : ObjectId("54f5b87cd4c6959bd3ecf2d6") },
"o" : { "$set" : { "ingredients.1.ingredid" : "54f5c117d4c6959bd3ecf2e8" } }
}
When my application starts I create a list of products which I get from the MongoDB. I just use products.findAll() and the put them in a List<'products>
I have several clients connected to the MongoDB and every client gets notified via oplog.
Now I want to update my local lists without querying the MongoDB since I have all the information needed to update my local products already in the oplog.
My question is now: How to do that?
My approach is the following:
Deserialize the object to Json
var prodjson= Product[0].ToJson<ProductModel>();
Convert Json to BsonDocument
var prodbson= BsonSerializer.Deserialize<BsonDocument>(prodjson);
Apply the "o" key from the oplog to the prodbson
"o" : { "$set" : { "ingredients.1.ingredid" : "54f5c117d4c6959bd3ecf2e8" } }
NO IDEA

Alter Json or ExtensionDataObject

I have a Json service I cannot alter as it is not mine.
Their Json is a formatted in a way that parsing it is difficult. It looks something like this.
"people": {
"Joe Bob": {
"name": "Joe Bob",
"id": "12345"
},
"Bob Smith": {
"name": "Bob Smith",
"id": "54321"
}
},
I would really prefer this was laid out like a JSon array, however it presently is not.
I am wondering the best approach here. Should I alter the Json to look like an array before I parse it or load up the ExtensionData and parse it from that?
There are other items in the feed that I do not have issue with. Just stuck with this one section.
Thanks
You can use json.net to deserialize the data (the json you pasted, and doing only one parsing, without modifying anything).
using dynamic foo = JsonConvert.DeserializeObject<dynamic>(data)
than, you can iterate the list using foo.people, accessing the Name and Value.
you can create a class (if you know what the schema is, and to deserialize the data into a list of the given class such as:
public class People
{
[JsonProperty(PropertyName="people")]
public IDictionary<string, Person> Persons { get; set; }
}
public class Person
{
[JsonProperty(PropertyName="name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
}
and than call:
var obj = JsonConvert.DeserializeObject<People>(data);
foreach (var item in obj.Persons.Values)
{
//item is instance of Person
}
Another good and possible option will be:
How can I navigate any JSON tree in c#?

Categories