I receive a JSon string from WS. It's so long that I can't use Json2charp to parse it and receive the structurated class.
I want to parse the string with a command. How is it possible?
I don't know the classes so I can't use a command like:
Dim result = JsonConvert.DeserializeObject(Of MyClass.RootObject)(String_From_File)
Is it possible from the string to obtain the class without using json2charp site ?
For example, in vs.net if on the variable 'string_from_file' I choose 'Json Visualizer' and see all classes and data in correct mode.
How can I obtain the same in my code ?
I have installed Newtonsoft.json
If you cannot use the json to class mappers like NewtonSoft.Json. You can use the Windows.Data.Json api. It let you parse and extract the data you want from your JSON string.
JsonValue jsonValue = JsonValue.Parse("{\"Width\": 800, \"Height\": 600, \"Title\": \"View from 15th Floor\", \"IDs\": [116, 943, 234, 38793]}");
double width = jsonValue.GetObject().GetNamedNumber("Width");
double height = jsonValue.GetObject().GetNamedNumber("Height");
string title = jsonValue.GetObject().GetNamedString("Title");
JsonArray ids = jsonValue.GetObject().GetNamedArray("IDs");
You can find a sample in the Windows Universal Sample GitHub.
A complex object parsing is shown here. I've extracted the most relevant parts here. The JSON string is provided to the User constructor which is extracting what it needs and then delegating the parsing to the nested School constructor.
{
"id": "1146217767",
"phone": null,
"name": "Satya Nadella",
"education": [
{
"school": {
"id": "204165836287254",
"name": "Contoso High School"
},
"type": "High School"
},
{
"school": {
"id": "116138758396662",
"name": "Contoso University"
},
"type": "College"
}
],
"timezone": -8,
"verified": true
}
This JSON fragment is parsed with this code:
public User(string jsonString) : this()
{
JsonObject jsonObject = JsonObject.Parse(jsonString);
Id = jsonObject.GetNamedString(idKey, "");
IJsonValue phoneJsonValue = jsonObject.GetNamedValue(phoneKey);
if (phoneJsonValue.ValueType == JsonValueType.Null)
{
Phone = null;
}
else
{
Phone = phoneJsonValue.GetString();
}
Name = jsonObject.GetNamedString(nameKey, "");
Timezone = jsonObject.GetNamedNumber(timezoneKey, 0);
Verified = jsonObject.GetNamedBoolean(verifiedKey, false);
foreach (IJsonValue jsonValue in jsonObject.GetNamedArray(educationKey, new JsonArray()))
{
if (jsonValue.ValueType == JsonValueType.Object)
{
Education.Add(new School(jsonValue.GetObject()));
}
}
}
public School(JsonObject jsonObject)
{
JsonObject schoolObject = jsonObject.GetNamedObject(schoolKey, null);
if (schoolObject != null)
{
Id = schoolObject.GetNamedString(idKey, "");
Name = schoolObject.GetNamedString(nameKey, "");
}
Type = jsonObject.GetNamedString(typeKey);
}
If you cannot use the automatic mapping from NewtonSoft.Json, you have no other way than doing it yourself.
Is not so simple.
The Json i received is very complicated and have many class
So i can't use
double width = jsonValue.GetObject().GetNamedNumber("Width");
Inside class i have more ...
Related
update:
In newtonsoft, it works fine like this.
var saveObject = Newtonsoft.Json.JsonConvert.SerializeObject(root, Newtonsoft.Json.Formatting.Indented);
var root2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Node>(saveObject);
Here, the entire "tree"/graph is reloaded into root2. Is this behaviour accomplishable with System.Text.Json?
original question:
I have a data model (more precisely a DOM) where I have been using BinaryFormatter to serialize and deserialize the entire graph of connected various objects. It has worked flawless for years. Now, the binaryFormatter has been deprecated, and Microsoft appears to suggest that one should use Json for the job.
Using System.Text.Json serializing the dom, the entire graph is serialized easily, however, when deserializing, only the root level node is deserialized. Any objects hanging on the root node is not deserialized. What am I missing to reload the entire graph? Annotations on the list property? Could anyone guide me to a good example? Or, any other good suggestions to save a dom on disc? Thank you.
using System.Text.Json;
var root = new Node("root");
var a1 = new Node("a1");
var a2 = new Node("a2");
var b11 = new Node("b11");
var b12 = new Node("b12");
var b21 = new Node("b21");
var b22 = new Node("b22");
root.Nodes.Add(a1);
root.Nodes.Add(a2);
a1.Nodes.Add(b11);
a1.Nodes.Add(b12);
a2.Nodes.Add(b21);
a2.Nodes.Add(b22);
var options = new JsonSerializerOptions { WriteIndented = true, };
string saveFile = JsonSerializer.Serialize(root,options);
Console.WriteLine("Savefile:");
Console.WriteLine(saveFile);
var loadFile = JsonSerializer.Deserialize<Node>(saveFile,options)!;
Console.WriteLine();
Console.WriteLine("Loadfile:");
Console.WriteLine(loadFile);
public class Node
{
public Node(string name)
{
Name = name;
}
public string Name { get; set; }
public List<Node> Nodes { get; set; } = new List<Node>();
}
Output:
Savefile:
{
"Name": "root",
"Nodes": [
{
"Name": "a1",
"Nodes": [
{
"Name": "b11",
"Nodes": []
},
{
"Name": "b12",
"Nodes": []
}
]
},
{
"Name": "a2",
"Nodes": [
{
"Name": "b21",
"Nodes": []
},
{
"Name": "b22",
"Nodes": []
}
]
}
]
}
Loadfile:
Node
I have followed an example shown here
link
And i got the hang of it, i managed to create my own "Employee" entity and i found some dummy api data online to play with.
like this Problem is, the tornadofx throws null pointer error, and i think its because the rest response sends something like this
{
"status": "success",
"data": [
{
"id": "1",
"employee_name": "Tiger Nixon",
"employee_salary": "320800",
"employee_age": "61",
"profile_image": ""
},
but when i use mocky and provide JUST the json part
[
{
"id": "1",
"employee_name": "Tiger Nixon",
"employee_salary": "320800",
"employee_age": "61",
"profile_image": ""
},...]
it all works fine.
I think those additional fields "status" and "success" in response confuse the rest client of tornadofx, and i cant manage to get it to work, is there anyway to tell client to ignore every other fields besides those of json data.
All links are functional, so you can try yourself.
full working example
package com.example.demo.view
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import javafx.scene.layout.BorderPane
import tornadofx.*
import javax.json.JsonObject
class Employee (id:Int?=null , name: String? = null, age: Int?=null): JsonModel {
val idProperty = SimpleIntegerProperty(this, "id")
var id by idProperty
val ageProperty = SimpleIntegerProperty(this, "age")
var age by ageProperty
val employeeNameProperty = SimpleStringProperty(this, "name", name)
var name by employeeNameProperty
override fun updateModel(json: JsonObject) {
with(json) {
id = int("id")!!
age = int("employee_age")!!
name = string("employee_name")
}
}
override fun toJSON(json: JsonBuilder) {
with(json) {
add("id", id)
add("employee_name", name)
add("employee_age", age)
}
}
}
class PersonEditor : View("Person Editor") {
override val root = BorderPane()
val api : Rest by inject()
var persons = listOf(Employee(1,"John", 44), Employee(2,"Jay", 33)).observable()
val model = PersonModel(Employee())
init {
api.baseURI = "https://run.mocky.io/v3/"
val response = api.get("f17509ba-2d12-4c56-b441-69ab23302e43")
println(response.list())
println(response.list().toModel<Employee>()[0].name)
// print( p.get(1))
with(root) {
center {
tableview(response.list().toModel<Employee>()) {
column("Id", Employee::idProperty)
column("Name", Employee::employeeNameProperty)
column("Age", Employee::ageProperty)
// Update the person inside the view model on selection change
model.rebindOnChange(this) { selectedPerson ->
item = selectedPerson ?: Employee()
}
}
}
right {
form {
fieldset("Edit person") {
field("Id") {
textfield(model.id)
}
field("Name") {
textfield(model.name)
}
field("Age") {
textfield(model.age)
}
button("Save") {
enableWhen(model.dirty)
action {
save()
}
}
button("Reset").action {
model.rollback()
}
}
}
}
}
}
private fun save() {
// Flush changes from the text fields into the model
model.commit()
// The edited person is contained in the model
val person = model.item
// A real application would persist the person here
println("Saving ${person.employeeNameProperty} / ${person.ageProperty}")
}
}
class PersonModel(person: Employee) : ItemViewModel<Employee>(person) {
val id = bind(Employee::idProperty)
val name = bind(Employee::employeeNameProperty)
val age = bind(Employee::ageProperty)
}
if you replace base url and send request to http://dummy.restapiexample.com/api/v1/employees you will get an error that i am talking about
Your call to mocky returns a list, so .list() works fine. Your call to restapiexample, however, returns an object, not a list, so .list() won't do what you expect. You can probably use something like this, though I haven't tested it:
response.one().getJsonArray("data").toModel<Employee>()[0].name)
Further explanation:
If you're not familiar with the structure of JSON, check out the diagrams on the JSON homepage.
TornadoFX has two convenience functions for working with JSON returns: .list() and .one(). The .list() function will check if the result is a JsonArray. If so, it simply returns it. If it is instead a JsonObject, it wraps that object in a list and returns the new list.
In your case, since restapiexample is returning an object, the result of your call to .list() is a JsonArray with a single object. It looks something like this:
[
{
"status": "success",
"data": [...]
}
]
Obviously that single object cannot be converted to an Employee, so dereferencing anything off of it will result in a NullPointerException.
The .one() function on the other hand will check if the response is a JsonObject. If it is, it simply returns the object. If, however, the response is a JsonArray, it will take the first item from the array and return that item.
Is it possible to serialize optional values in F# using FsPickler such that:
when the value is Some(), the value contained is serialized
and when it is None, it does not get serialized at all?
With the following example code:
type Person = { name: string; age: int option }
let data = [
{ name = "Helena"; age = Some(24) };
{ name = "Peter"; age = None }
]
let jsonSerializer = FsPickler.CreateJsonSerializer(true, true)
let streamWriter = new StreamWriter(#"C:\output.json")
let out = jsonSerializer.SerializeSequence(streamWriter, data)
the output.json file contains the following JSON:
[
{
"name": "Helena",
"age": {
"Some": 24
}
},
{
"name": "Peter",
"age": null
}
]
But I would like the contents of JSON file to look like this instead:
[
{
"name": "Helena",
"age": 24
},
{
"name": "Peter"
}
]
I am using FsPickler.Json v3.2.0 and Newtonsoft.Json v9.0.1.
UPDATE (January 11, 2017): Using the script in the gist linked by Stuart in the answer below, I got it working like this:
let obj = { name = "Peter"; age = None }
let stringWriter = new StringWriter()
let jsonSerializer = new JsonSerializer()
jsonSerializer.Converters.Add(new IdiomaticDuConverter())
jsonSerializer.NullValueHandling <- NullValueHandling.Ignore
jsonSerializer.Serialize(stringWriter, obj)
Using Newtonsoft.Json you can use the gist here (credit: Isaac Abraham) to give you the behaviour you are after.
It's funny, just moments before you posted this, I was looking to see if the same thing exists within FsPickler.Json, but came to no conclusions. You could use TypeShape which is used in FsPickler to make the gist cleaner, but not sure if FsPickler.Json can do this for you out of the box.
I'm the author FsPickler, so thought I'd repost a response I gave in a similar issue.
No, managing the shape of the serialization formats is beyond the design goals of this library. While you could use pickler combinators to influence how serialized types look like, this will only take you so far.
With FSharp.Json library it would work just well:
open FSharp.Json
type Person = { name: string; age: int option }
let data = [
{ name = "Helena"; age = Some(24) };
{ name = "Peter"; age = None }
]
let json = Json.serialize data
This produces following JSON:
[
{ "name": "Helena", "age": 24 },
{ "name": "Peter", "age": null }
]
How option None is serialized is configurable. Here's how to omit the option member that has None value, pay attention to config:
let config = JsonConfig.create(serializeNone=SerializeNone.Omit)
let json = Json.serializeEx config data
Disclosure: I'm author of FSharp.Json library.
I'm currently working with trying to extract bits of information from a complicated json based database. After NSJSONSerialization.JSONObjectWithData I get output like follows (some returns added for clarity)
[
"title": Recorder Suite In A Minor - Viola Concerto - Tafelmusik,
"estimated_weight": 85,
"year": 0,
"thumb": ,
"identifiers": <__NSArrayI 0x600000089970>(
{
description = Text;
type = Barcode;
value = 4891030501560;
},
{
description = Printed;
type = Barcode;
value = "4 891030 501560";
},
{
type = ASIN;
value = B0000013L9;
},
{
type = "Mould SID Code";
value = "ifpi 8412";
},
{
type = "Matrix / Runout";
value = "CD PLANT AB 8550156 CDM01";
},
{
description = "SPARS Code";
type = Other;
value = DDD;
},
{
type = "Label Code";
value = "LC 9158";
}
),
"id": 885370,
"date_changed": 2014-06-17T03:53:03-07:00,
"master_url": https://api.discogs.com/masters/495830,
etc … ]
In particular, I need to know how to get the information out of the nested array. Note that the array is not (obviously) a nested dictionary - given the equal signs and the repeated keys. Any help with how to parse this would be appreciated.
I would use a Pod like SwiftyJSON.
First, you need to install CocoaPods, and then go for SwiftyJSON.
I would parse nested arrays in the following manner:
let json = JSON(data: dataFromNetworking)
if let items = json["items"].array {
for item in items {
if let title = item["title"].string {
println(title)
}
}
}
Check out the documentation and Usage section of SwiftyJSON for more info.
Cheers...
I have JSON that looks like this:
{
"status": {
"code": 0,
"message": "OK"
},
"data": {
"_idtype": "cusip",
"_id": "00768Y883",
"api": {
"_name": "PortfolioBreakdownsRaw",
"PortfolioDate": "2015-10-12",
"GlobalBondSuperSectorLongSalePositionBreakdown": [
{
"Name": "Municipal",
"Value": "0.57842"
},
{
"Name": "Corporate",
"Value": "1.79649"
},
{
"Name": "Securitized",
"Value": "5.29493"
},
{
"Name": "Cash & Equivalents",
"Value": "166.20776"
}
],
"GlobalBondSuperSectorShortSalePositionBreakdown": [
{
"Name": "Government",
"Value": "0.90557"
}
]
}
}
}
I am able to get the api portion of the response easily:
var jObject = JObject.Parse(json);
var api = jObject["data"]["api"];
From here, I don't what if any arrays will be included in the response. The ultimate goal will be to create a parser that will be able to get the array names (GlobalBondSuperSectorShortSalePositionBreakdown) and as many rows of key-value pairs that it may contain, without first knowing the names such as (GlobalBondSuperSectorShortSalePositionBreakdown) beforehand.
I can't seem to find a good way to loop through the object, determine there are arrays at the api level and then iterate through those to get the values.
Any help would be appreciated.
Here's an example. In this code, the api variable holds a JObject, so we can iterate over its properties. From there, we look at the Type of each property value to see if it is an array or not. If it is, then we can iterate over that array to get the JObjects within it, and extract the Name and Value values that we expect to find there. Does this help?
var jObject = JObject.Parse(json);
var api = jObject["data"]["api"];
foreach (JProperty prop in api.Children<JProperty>())
{
JToken value = prop.Value;
if (value.Type == JTokenType.Array)
{
Console.WriteLine(prop.Name + ": ");
foreach (JObject jo in value.Children<JObject>())
{
Console.WriteLine(" " + jo["Name"] + ": " + jo["Value"]);
}
}
else
{
Console.WriteLine(prop.Name + ": " + value);
}
}
Output:
_name: PortfolioBreakdownsRaw
PortfolioDate: 2015-10-12
GlobalBondSuperSectorLongSalePositionBreakdown:
Municipal: 0.57842
Corporate: 1.79649
Securitized: 5.29493
Cash & Equivalents: 166.20776
GlobalBondSuperSectorShortSalePositionBreakdown:
Government: 0.90557
Fiddle: https://dotnetfiddle.net/XyoXQy
With Linq you can play pretty nice with Json.net:
Here is an easily readable version of the chunk of code that will create two dictionaries out of the JArray properties under the api element:
var api = jObject["data"]["api"];
var arrays = api.Cast<JProperty>().Where(o => o.Value.Type == JTokenType.Array).Select(token => token.Value).ToArray();
var dictionaries = new List<Dictionary<string, string>>();
foreach (var array in arrays)
{
var dictionary = array.ToDictionary(token => token["Name"].Value<string>(), token => token["Value"].Value<string>());
dictionaries.Add(dictionary);
}
alternative:
The same thing, but a shorter, more compact version :
var api = jObject["data"]["api"];
var dictionaries = api
.Cast<JProperty>()
.Where(o => o.Value.Type == JTokenType.Array)
.Select(token => token.Value)
.Select(array => array.ToDictionary(token => token["Name"].Value<string>(), token => token["Value"].Value<string>()));