Making JSON object with Arrays - json

I'm looking to create what I feel is probably a basic JSON object, but my limited knowledge of JSON is making it difficult.
I am trying to create an object that will ultimately be passed to a .NET AMSX web service. The parameter for the web service is a P1Request object, which is defined as follows:
Public Class P1RequestClause
Public Property FieldId() As Integer
Public Property OperatorId() As Integer
Public Property Value() As String
End Class
Public Class P1Request
Public Property Fields() As String()
Public Property Clauses() As P1RequestClause()
End Class
On the client side, I have a number of different form fields, the values of which I would like to wrap up in a JSON object to pass.
I am unsure of what structure my JSON object needs to match the .NET class.
Ideally my data, in psudocode, would look like:
P1Request:
Fields:
Field1,
Field2,
Field3
Clauses:
P1RequestClause:
Id1,
OpId1,
SomeValue
P1RequestClause:
Id2,
Opid2,
AnotherValue
What would this look like in JSON? It's the array of Fields in P1Request is the part that confuses me the most. As I understand JSON, it's all name:value pairs, and making an array of a single field is throwing me.

{
"Fields": [
"moo",
"says",
"the cow"
],
"Clauses": [
{
"FieldId": 1,
"OperatorId": 3,
"Value": "foo"
},
{
"FieldId": 2,
"OperatorId": 0,
"Value": "bar"
}
]
}
JSON consists of primitive types (numbers, strings, null...), objects (which are key-value pair collections), and arrays, which is what you were missing.

Related

How to extract value from nested JSON data in Spring Boot

Currently, I am calling an API every 3 minutes to get weather data. This is the response I get as a String:
{
"data": {
"timelines": [
{
"timestep": "current",
"endTime": "2023-01-20T13:33:00-05:00",
"startTime": "2023-01-20T13:33:00-05:00",
"intervals": [
{
"startTime": "2023-01-20T13:33:00-05:00",
"values": {
"dewPoint": 18.63,
"humidity": 66,
"precipitationIntensity": 0,
"precipitationProbability": 0,
"pressureSeaLevel": 1019.19,
"pressureSurfaceLevel": 1018.65,
"temperature": 25.5,
"temperatureApparent": 25.5,
"uvIndex": 3,
"windDirection": 151.69,
"windSpeed": 4.88
}
}
]
}
]
}
}
I would like to know how to extract the value of the attributes in "values" (like "dewPoint", "humidity", etc.).
I've been trying to do this with Beans and such, but the fact that the data is nested and some of it is an array is making it difficult for me to understand.
Basically, I want to extract each of the values of the attributes in "values" from a JSON String, concat them in to an array of only the values, and then send that array to be saved in a database.
You will first need to create the classes to model this and then take the string and use an Object mapper to take a string and convert it to an object.
class WeatherData {
List<Timelines> timelines; // all other object are nested in here.
}
class Timelines {
String timestep;
String endTime;
String startTime;
List<Intervals> intervals;
}
class Intervals {
String startTime;
Values values;
}
class Values {
double dewPoint;
int humidity;
int precipitationIntensity;
int precipitationProbability;
double pressureSeaLevel;
double pressureSurfaceLevel;
double temperature;
double temperatureApparent;
int uvIndex;
double windDirection;
double windSpeed;
}
WeatherData data = new ObjectMapper().readValue(jsonString, WeatherData.class);
There are several ways to approach this once you map the json to an Object. You can loop through the values, so two for loops. Once you get to the intervals you can add the values to the array.
List<WeatherData> newWeatherDataList = new ArrayList();
The other way would be to flatten down the object so it's not so nested. I'd suggest looking up how to flatten an object in Java. You will still need one for loop to loop through the intervals array. Then just add it to the new list like above.
From here you can batch-insert the data into a table.
I hope this helps let me know if I can update the answer to provide more context.

How to Import JSON data to typescript Map<K, V> type?

I am trying to import json data that might include present or absent mappings within one of the properties, and figured the correct data type to represent this was Map<string, number>, but I'm getting an error when I try to do this.
My JSON file, data.json, looks like this:
{
"datas": [
{
"name":"test1",
"config":"origin",
"entries": {
"red":1,
"green":2
}
}
,
{
"name":"test2",
"config":"remote",
"entries": {
"red":1,
"blue":3
}
}
,
{
"name":"test3",
"entries": {
"red":1,
"blue":3,
"purple":3
}
}
]
}
My typescript code, Data.ts, which attempts to read it looks like this:
import data from './data.json';
export class Data {
public name:string;
public config:string;
public entries:Map<string, number>;
constructor(
name:string,
entries:Map<string, number>,
config?:string
) {
this.name = name;
this.entries = entries;
this.config = config ?? "origin";
}
}
export class DataManager {
public datas:Data[] = data.datas;
}
But that last line, public datas:Data[] = data.datas;, is throwing an error.
Is there a proper way to import data like this?
The goal, ultimately, is to achieve three things:
Any time entries is present, it should receive some validation that it only contains properties of type number; what those properties are is unknown to the programmer, but will be relevant to the end-user.
If config is absent in the JSON file, the construction of Data objects should supply a default value (here, it's "origin")
This assignment of the data should occur with as little boilerplate code as possible. If, down the line Data is updated to have a new property (and Data.json might or might not receive updates to its data to correspond), I don't want to have to change how DataManager.data receives the values
Is this possible, and if so, what's the correct way to write code that will do this?
The lightest weight approach to this would not be to create or use classes for your data. You can instead use plain JavaScript objects, and just describe their types strongly enough for your use cases. So instead of a Data class, you can have an interface, and instead of using instances of the Map class with string-valued keys, you can just use a plain object with a string index signature to represent the type of data you already have:
interface Data {
name: string;
config: string;
entries: { [k: string]: number }
}
To make a valid Data, you don't need to use new anywhere; just make an object literal with name, config, and entries properties of the right types. The entries property is { [k: string]: number }, which means that you don't know or care what the keys are (other than the fact that they are strings as opposed to symbols), but the property values at those keys should be numbers.
Armed with that definition, let's convert data.datas to Data[] in a way that meets your three criteria:
const datas: Data[] = data.datas.map(d => ({
config: "origin", // any default values you want
...d, // the object
entries: onlyNumberValues(d.entries ?? {}) // filter out non-numeric entries
}));
function onlyNumberValues(x: { [k: string]: unknown }): { [k: string]: number } {
return Object.fromEntries(
Object.entries(x).filter(
(kv): kv is [string, number] => typeof kv[1] === "number"
)
);
}
The above sets the entries property to be a filtered version of the entries property in the incoming data, if it exists. (If entries does not exist, we use an empty object {}). The filter function is onlyNumberValues(), which breaks the object into its entries via the Object.entries() method, filters these entries with a user-defined type guard function, and packages them back into an object via the Object.fromEntries() method. The details of this function's implementation can be changed, but the idea is that you perform whatever validation/transformation you need here.
Any required property that may be absent in the JSON file should be given a default value. We do this by creating an object literal that starts with these default properties, after which we spread in the properties from the JSON object. We do this with the config property above. If the JSON object has a config property, it will overwrite the default when spread in. (At the very end we add in the entries property explicitly, to overwrite the value in the object with the filtered version).
Because we've spread the JSON object in, any properties added to the JSON object will automatically be added. Just remember to specify any defaults for these new properties, if they are required.
Let's make sure this works as desired:
console.log(datas)
/* [{
"config": "origin",
"name": "test1",
"entries": {
"red": 1,
"green": 2
}
}, {
"config": "remote",
"name": "test2",
"entries": {
"red": 1,
"blue": 3
}
}, {
"config": "origin",
"name": "test3",
"entries": {
"red": 1,
"blue": 3,
"purple": 3
}
}] */
Looks good.
Playground link to code

Unable to deserialize a JSON array with Json.Net

I have a JSON like this:
[
{
"areas": [
{
"area": "New York",
"isDay": true,
"temp": "14"
},
{
"area": "Washington DC",
"isDay": true,
"temp": "30"
},
{
"area": "Los Angles",
"isDay": false,
"temp": "54"
},
{
"area": "San Diego",
"isDay": true,
"temp": "59"
}
],
"status": true,
"code": 200,
"created_at": "2019/06/18 22:26:34.475",
"request_time": "2019/06/18 22:25:28.306"
}
]
I'm trying to get the value of the the area value on the first/second/third object under the Areas array.
I've tried this code to get the value of the first area of the array, which in this case is "New York".
'Note that 'rawJSON' is a string variable that contains the JSON.
Dim jResults As JObject = JObject.Parse(rawJSON)
Dim naming As String = jResults("areas")("name")
However when I run this, I get and exception:
Newtonsoft.Json.JsonReaderException: 'Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.'
I am trying to write a function that imports all 'areas' in each array with their identifier of area (such as New York/Washington DC) into a ListBox (so the ListBox will contain, in order: New York, Washington DC, Los Angles).
How can I do this?
There are a couple of problems here:
Your JSON begins and ends with square brackets, so that means it is an array, not a single object. Since it is an array you need to use JArray.Parse() to parse it instead of JObject.Parse().
Inside your array, there is a single object which contains a property areas along with a few other properties like status and code. The value of the areas property is also an array, as denoted by square brackets. So your code needs to account for both of those arrays by adding the indexes. If you want the first item, that would be index zero.
There is no property called name anywhere in the JSON you posted. Instead, the names of the cities are each in a property called area.
Putting it all together, if you want to get the name New York from this JSON, you would do it like this:
Dim jResults As JArray = JArray.Parse(rawJSON)
Dim name As String = jResults(0)("areas")(0)("area")
Demo: https://dotnetfiddle.net/eAfEsx
If you're trying to get multiple names, then you would need a loop. You could do something like this:
Dim names As New List(Of String)()
For Each area As JObject In jResults(0)("areas")
Dim name As String = area("area")
names.Add(name)
Next
Demo: https://dotnetfiddle.net/BtSa6O
As you can see from the JSON structure you have posted, the JSON starts with a square bracket. That's the start of an array, the StartArray: when the JsonReader starts reading the JSON, the first TokenType is a JsonToken.StartArray (what the error is telling you).
Then there's an Object, defined by the braces: { }.
So you have an Array or List of Objects.
The first property of this object (areas) is of Type Array (or List).
It defines a collection of Objects.
Other properties, (status, code, etc.) have a single value.
[
{
"areas": [ { ... }, { ... } ]
"other": "other value"
}
]
You can parse the outer Array with JArray. The first element in the array (and the only one you're showing here), is the outer array. The first property of the first element in the outer array is the areas Array.
So you could write:
Dim areasArray = JArray.Parse(rawJson)(0)("areas")
Dim firstAreaValue = areasArray(0)("area").ToString()
The value of firstAreaValue is then "New York".
This is can be tiresome and prone to errors. You can generate a class Model from the JSON and use this model to deserialize the JSON to .Net classes, making it easier to handle, use as Source of Data, modify, serialize back etc.
Your JSON is an Array of Objects, so let's build the Root Object as a .Net class:
Note that I've set the Type of the CreatedAt and RequestTime properties to DateTime (Date): the string format of the DateTime, "2019/06/18 22:26:34.475", can be deserialized correctly.
DateTimeOffset is probably better. Your choice.
Public Class AreasRoot
<JsonProperty("areas")>
Public Property Areas As List(Of AreaObject)
<JsonProperty("status")>
Public Property Status As Boolean
<JsonProperty("code")>
Public Property Code As Long
<JsonProperty("created_at")>
Public Property CreatedAt As DateTime
<JsonProperty("request_time")>
Public Property RequestTime As DateTime
End Class
The Areas property is an Array or List of another type of object that contains 3 properties:
Public Class AreaObject
<JsonProperty("area")>
Public Property AreaArea As String
<JsonProperty("isDay")>
Public Property IsDay As Boolean
<JsonProperty("temp")>
Public Property Temp As Long
End Class
To deserialize using this Model, call JsonConvert.DeserializeObject(Of Type)(json).
The Type is, as mentioned, an Array or List(Of RootObject):
Dim areasArray = JsonConvert.DeserializeObject(Of List(Of AreasRoot))(rawJson).First()
Now New York is:
Dim firstArea = areasArray.Areas.First().Area
' Or
Dim firstArea = areasArray.Areas(0).Area
You can list all the Area names with:
For Each areaObj In areasArray.Areas
Console.WriteLine(areaObj.Area)
Next

obtain values to json file

I am making an application in vb net, and read JSON format files.
I know how, but I have a file that has a different format, you can see here:
{
"objects": {
"realms/lang/de_DE.lang": {
"hash": "729b2c09d5c588787b23127eeda2730f9c039194",
"size": 7784
},
"realms/lang/cy_GB.lang": {
"hash": "7b52463b2df4685d2d82c5d257fd5ec79843d618",
"size": 7688
},
"minecraft/sounds/mob/blaze/breathe4.ogg": {
"hash": "78d544a240d627005aaef6033fd646eafc66fe7a",
"size": 22054
},
"minecraft/sounds/dig/sand4.ogg": {
"hash": "37afa06f97d58767a1cd1382386db878be1532dd",
"size": 5491
}
}
}
It is different because all text has objects, no string, so I can not read it defined classes.
I just need the values of "hash", then add them to a textbox.
I hope you can help me.
Sounds like #Plutonix has seen this before, so I'll go with the Minecraft thing. It seems like objects is a Dictionary(Of String, MinecraftItem). A little tweak to the linked duplicate could be to create a class for the whole file, so you don't have to pull out the objects separately:
Public Class MinecraftData
Public Property Objects As Dictionary(Of String, MinecraftItem)
End Class
Public Class MinecraftItem
Public Property Hash As String
Public Property Size As Long
End Class
You can then parse the whole thing using Json.NET:
Dim data = JsonConvert.DeserializeObject(Of MinecraftData)(json)
If you are certain that you will only ever need the hashes, you can actually use a regex to get all the hashes from this file. Yes, this is slightly evil, because regex is normally not a suitable tool for dealing with structured data. But if you don't care about the structure...
Dim hashExtractor = new System.Text.RegularExpressions.Regex("[0-9a-f]{40}")
Dim matches = hashExtractor.Matches(myFileContents).Cast(of System.Text.RegularExpressions.Match)
Dim hashes = matches.Select(Function(m) m.Value).ToList()

Representing variable object arrays in JSON

Is there a way to represent an object array in JSON? e.g. Supposing I have the server-side method Test(object[]) which expects any of the three following classes:
class ObjectA{ int a; }
class ObjectB{ bool a; }
class ObjectC{ string a; }
What would new object[]{ new ObjectA(), new ObjectB(), new ObjectC() } look like when represented in JSON?
I guess it would be:
[{"a": 42}, {"a": true}, {"a": "The answer is"}]
The only way to preserve the type of object, would be to add an additional property. JSON does not know anything about classes. There are only objects which have certain properties.