Rest Assured - Json - How to create a Map<time,count> from below JSON response? - collections

API response json donot have any array name, it only has elements as time and count.
How can I store time and count in a Map.
Below is below Response JSON:
[ { "_time": "2021-10-28T00:00:00", "count": 10030.0 }, { "_time": "2021-10-29T00:00:00", "count": 7776.0 } ]
I have tried creating two list for _time and count , then storing it in a Map.
JsonPath js = new JsonPath(res.asString());
List<Float> count = js.getList("count");
List<String> time = js.getList("_time");
Map<String,Float> map = new LinkedHashMap<String,Float>();
Iterator<String> keyIter = time.iterator();
Iterator<Float> valIter = count.iterator();
while (keyIter.hasNext() && valIter.hasNext()) {
map.put(keyIter.next(), valIter.next());
}
But is there any other optimized way to do so?

I use this way.
Step 1: Deserialize response to POJO
import lombok.Data;
#Data
public class TimeObject {
private String _time;
private double count;
}
List<TimeObject> timeObjects = JsonPath.with(res.asString()).getList("", TimeObject.class);
Step2: Use java stream to collect a Map.
Map<String, List<Double>> collect = timeObjects.stream()
.collect(groupingBy(TimeObject::get_time,
mapping(TimeObject::getCount, toList())));
System.out.println(collect);
//{2021-10-28T00:00:00=[10030.0], 2021-10-29T00:00:00=[7776.0]}

Related

How convert IConfigurationRoot or IConfigurationSection to JObject/JSON

I have the following code in my Program.cs:
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("clientsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"clientsettings.{host.GetSetting("environment")}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
I want to convert the result of building my configuration to JObject\Json for sending to the client. How can I do it?
and I don't want to create my custom class for my settings.
My answer: merge
public static JObject GetSettingsObject(string environmentName)
{
object[] fileNames = { "settings.json", $"settings.{environmentName}.json" };
var jObjects = new List<object>();
foreach (var fileName in fileNames)
{
var fPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + fileName;
if (!File.Exists(fPath))
continue;
using (var file = new StreamReader(fPath, Encoding.UTF8))
jObjects.Add(JsonConvert.DeserializeObject(file.ReadToEnd()));
}
if (jObjects.Count == 0)
throw new InvalidOperationException();
var result = (JObject)jObjects[0];
for (var i = 1; i < jObjects.Count; i++)
result.Merge(jObjects[i], new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Merge
});
return result;
}
Since configuration is actually just a key value store where the keys have a certain format to represent a path, serializing it back into a JSON is not that simple.
What you could do is recursively traverse through the configuration children and write its values to a JObject. This would look like this:
public JToken Serialize(IConfiguration config)
{
JObject obj = new JObject();
foreach (var child in config.GetChildren())
{
obj.Add(child.Key, Serialize(child));
}
if (!obj.HasValues && config is IConfigurationSection section)
return new JValue(section.Value);
return obj;
}
Note that this is extremely limited in how the output looks. For example, numbers or booleans, which are valid types in JSON, will be represented as strings. And since arrays are represented through numerical key paths (e.g. key:0 and key:1), you will get property names that are strings of indexes.
Let’s take for example the following JSON:
{
"foo": "bar",
"bar": {
"a": "string",
"b": 123,
"c": true
},
"baz": [
{ "x": 1, "y": 2 },
{ "x": 3, "y": 4 }
]
}
This will be represented in configuration through the following key paths:
"foo" -> "bar"
"bar:a" -> "string"
"bar:b" -> "123"
"bar:c" -> "true"
"baz:0:x" -> "1"
"baz:0:y" -> "2"
"baz:1:x" -> "3"
"baz:1:y" -> "4"
As such, the resulting JSON for the above Serialize method would look like this:
{
"foo": "bar",
"bar": {
"a": "string",
"b": "123",
"c": "true"
},
"baz": {
"0": { "x": "1", "y": "2" },
"1": { "x": "3", "y": "4" }
}
}
So this will not allow you to get back the original representation. That being said, when reading the resulting JSON again with Microsoft.Extensions.Configuration.Json, then it will result in the same configuration object. So you can use this to store the configuration as JSON.
If you want anything prettier than that, you will have to add logic to detect array and non-string types, since both of these are not concepts of the configuration framework.
I want to merge appsettings.json and appsettings.{host.GetSetting("environment")}.json to one object [and send that to the client]
Keep in mind that environment-specific configuration files often contain secrets that shouldn’t leave the machine. This is also especially true for environment variables. If you want to transmit the configuration values, then make sure not to include the environment variables when building the configuration.
The configuration data is represented by a flattened collection of KeyValuePair<string, string>. You could create a dictionary from it and serialize that to JSON. However, that will probably not give you the desired result:
Configuration.AsEnumerable().ToDictionary(k => k.Key, v => v.Value);
Also, please take in mind that this configuration object will contain environment variables, you definitely don't want to send these to the client.
A better option might be to first bind the configuration to your POCO's and serialize those to JSON:
var appConfig = new AppConfig();
Configuration.Bind(appConfig);
var json = JsonConvert.SerializeObject(appConfig);
public class AppConfig
{
// Your settings here
public string Foo { get; set; }
public int Bar { get; set; }
}
The resultant IConfiguration object from the Build() method will encompass all of your configuration sources, and will merge based on the priority order defined by the order in which you added your config sources.
In your case this would be:
clientsettings.json
clientsettings.env.json
Environment Variables
You wont need to worry about merging sources manually or loading the files, as its already done for you.
To improve on poke's answer, I came up with this:
private JToken Serialize(IConfiguration config)
{
JObject obj = new JObject();
foreach (var child in config.GetChildren())
{
if (child.Path.EndsWith(":0"))
{
var arr = new JArray();
foreach (var arrayChild in config.GetChildren())
{
arr.Add(Serialize(arrayChild));
}
return arr;
}
else
{
obj.Add(child.Key, Serialize(child));
}
}
if (!obj.HasValues && config is IConfigurationSection section)
{
if (bool.TryParse(section.Value, out bool boolean))
{
return new JValue(boolean);
}
else if (decimal.TryParse(section.Value, out decimal real))
{
return new JValue(real);
}
else if (long.TryParse(section.Value, out int integer))
{
return new JValue(integer);
}
return new JValue(section.Value);
}
return obj;
}
The code above accounts for data types such as boolean, long & decimal.
long & decimal are the largest data types available for integers so will encompass any smaller values like short or float.
The code will also construct your arrays properly, so you end up with a like for like representation of all of your config in one json file.
Here is Tom's solution converted to use System.Text.Json.
static internal JsonNode? Serialize(IConfiguration config)
{
JsonObject obj = new();
foreach (var child in config.GetChildren())
{
if (child.Path.EndsWith(":0"))
{
var arr = new JsonArray();
foreach (var arrayChild in config.GetChildren())
{
arr.Add(Serialize(arrayChild));
}
return arr;
}
else
{
obj.Add(child.Key, Serialize(child));
}
}
if (obj.Count() == 0 && config is IConfigurationSection section)
{
if (bool.TryParse(section.Value, out bool boolean))
{
return JsonValue.Create(boolean);
}
else if (decimal.TryParse(section.Value, out decimal real))
{
return JsonValue.Create(real);
}
else if (long.TryParse(section.Value, out long integer))
{
return JsonValue.Create(integer);
}
return JsonValue.Create(section.Value);
}
return obj;
}
// Use like this...
var json = Serialize(Config);
File.WriteAllText("out.json",
json.ToJsonString(new JsonSerializerOptions() { WriteIndented = true}));
Do you really want to sent to client all your environment variables (.AddEnvironmentVariables()), connections string and all other stuff in appsettings??? I recommend you do not do this.
Instead, make one class (say ClientConfigOptions), configure it binding using services.Configure<ClientConfigOptions>(configuration.GetSection("clientConfig")) and send it to client.
With this approach, you may also tune your ClientConfigOptions with Actions, copy some values from different appsetting paths, etc.

Is there a way to insert a document with a nested array in Azure Data Factory?

I am trying to add documents in CosmosDb that has a nested array. I am using the Copy Activity.
Sample Document:
{
"itemNumber": "D10001",
"readings" : [
{ "value": 25, "ets":"100011111"},
{ "value": 35, "ets":"100011122"}
]
}
In the source dataset I formatted the readings array as a string in my SQL query, and set the data type in the sink dataset as an Object. The data is copied, but the readings are stringified.
Is there a means to configure the Copy Activity to handle this array?
What is your source? You could copy your data to json files first. And then import it to cosmos DB as is, which means don’t specify the format and structure in your source and sink dataset.
As I know, no such properties could help you convert string data to array format in adf cosmos db configuration.
Since you are using adf to import data so that you can't use PreTrigger to change the format of created documents.PreTrigger need to be invoked by code or rest api.
So, as workaround, I suggest you using Azure Function Cosmos DB Trigger to process every document when they imported into database. Please refer to my function code:
using System.Collections.Generic;
using Microsoft.Azure.Documents;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json.Linq;
using System;
using Microsoft.Azure.Documents.Client;
namespace TestADF
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([CosmosDBTrigger(
databaseName: "db",
collectionName: "item",
ConnectionStringSetting = "documentdbstring",
LeaseCollectionName = "leases")]IReadOnlyList<Document> input, TraceWriter log)
{
if (input != null && input.Count > 0)
{
log.Verbose("Start.........");
String endpointUrl = "https://***.documents.azure.com:443/";
String authorizationKey = "key";
String databaseId = "db";
String collectionId = "item";
DocumentClient client = new DocumentClient(new Uri(endpointUrl), authorizationKey);
for (int i = 0; i < input.Count; i++)
{
Document doc = input[i];
if ((doc.GetPropertyValue<Boolean>("alreadyForamt") == null) || (!doc.GetPropertyValue<Boolean>("alreadyForamt")))
{
String readings = doc.GetPropertyValue<String>("readings");
JArray r = JArray.Parse(readings);
doc.SetPropertyValue("readings", r);
client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, doc.Id), doc);
log.Verbose("Update document Id " + doc.Id);
}
}
}
}
}
}
Hope it helps you.

How to parse a Spring 5 WebClient response in a non-blocking way?

I'm using Spring WebFlux WebClient to retrieve data from an external API, like this:
public WeatherWebClient() {
this.weatherWebClient = WebClient.create("http://api.openweathermap.org/data/2.5/weather");
}
public Mono<String> getWeatherByCityName(String cityName) {
return weatherWebClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("q", cityName)
.queryParam("units", "metric")
.queryParam("appid", API_KEY)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class);
}
This works fine and produces a response like this:
{
"coord":{
"lon":-47.06,
"lat":-22.91
},
"weather":[
{
"id":800,
"main":"Clear",
"description":"clear sky",
"icon":"01d"
}
],
"base":"stations",
"main":{
"temp":16,
"pressure":1020,
"humidity":67,
"temp_min":16,
"temp_max":16
},
"visibility":10000,
"wind":{
"speed":1,
"deg":90
},
"clouds":{
"all":0
},
"dt":1527937200,
"sys":{
"type":1,
"id":4521,
"message":0.0038,
"country":"BR",
"sunrise":1527932532,
"sunset":1527971422
},
"id":3467865,
"name":"Campinas",
"cod":200
}
But I'm only interested in the "temp" property (main -> temp). How could I transform the response (using Jackson's ObjectMapper, for example) to return only "temp" value in a reactive/non-blocking way?
I understand the first thing is replacing ".retrieve()" by ".exchange()" but I can't figure out how to make it work.
PS: This is my first question here. Please let me know if I'm doing something wrong or if you need more details.
Thanks!
You need to create a type that corresponds to the response sent by the server. A very minimal example could be like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherResponse {
public MainWeatherData main;
}
and the MainWeatherData class could be:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MainWeatherData {
public String temp;
}
Finally, you could use WeatherResponse in bodyToMono:
...
.retrieve()
.bodyToMono(WeatherResponse.class);
The #JsonIgnoreProperties(ignoreUnknown = true)annotation instructs Jackson to not give any errors if it encounters any value in JSON string that is not present in you POJO.
You can access the WeatherResponseobject with a chained map operator:
getWeatherByCityName(cityName)
.map(weatherResponse -> weatherResponse.main.temp)

Format the nested property serialized json

I have a CSV string, and one of it's column value is json serialized.
"Id,Name,Seo\r\n13,SpecialCollections,\"{\"\"SeoUrl\"\":\"\"special-collections\"\",\"\"SeoPageTitle\"\":null,\"\"SeoKeywords\"\":null,\"\"SeoDescription\"\":null}\"\r\n";
I'm using a combination of JSON.NET and ServiceStack.Text to serialize and deserialize my data from json-csv and vice versa.
So, with the above CSVinput, I first convert it to .NET object using ServiceStack.Text helper method
var obj= csvInput.FromCsv<List<dynamic>>();
and that gives me an output in a form of Dictionary<string,string> for each row of csv
1) {[Id, 13]}
2) {[Name, SpecialCollections]}
3) {[Seo, {"SeoUrl":"special-collections","SeoPageTitle":null,"SeoKeywords":null,"SeoDescription":null}]}
Then I serialized the above output with JSON.NET helper method and write in a file which looks like this
var serializedJson = JsonConvert
.SerializeObject(obj, Formatting.Indented);
Result
[
{
"Id": "13",
"Name": "SpecialCollections",
"Seo": "{\"SeoUrl\":\"special-collections\",\"SeoPageTitle\":null,\"SeoKeywords\":null,\"SeoDescription\":null}"
}
]
The issue is with nested property 'Seo', although it's value is serialized json but that is because it is string, JSON.NET treat as a string and doesn't format it. Anyway, I can obtain the below expected result?
Expected Result:
[
{
"Id": "13",
"Name": "SpecialCollections",
"Seo": {
"SeoUrl": "special-collections",
"SeoPageTitle": null,
"SeoKeywords": null,
"SeoDescription": null
}
}
]
Any help on this would be highly appreciated.
Since your "Seo" value is already a JSON string, you'll need to deserialize it into a temporary object (such as a JObject) then recombine it with the other key-value pairs into a new container and serialize that to get the final result you want. Here is a simple way to do that.
First, create a helper method which can determine whether a string value is JSON or not, and return a JToken from it.
public static JToken ToJToken(string s)
{
if (s == null)
return JValue.CreateNull();
// if the string is already JSON, parse it into a JObject (or JArray)
if ((s.StartsWith("{") && s.EndsWith("}")) || (s.StartsWith("[") && s.EndsWith("]")))
return JToken.Parse(s);
// otherwise create a JValue from the non-JSON string
return JToken.FromObject(s);
}
Then, convert your List<Dictionary<string, string>> into a JArray using the above helper method like this:
JArray ja = new JArray(
obj.Select(
dict => new JObject(
((Dictionary<string, string>)dict).Select(
kvp => new JProperty(kvp.Key, ToJToken(kvp.Value))
)
)
)
);
Now, to get the formatted JSON you can simply call ToString() on the JArray:
string json = ja.ToString();
Fiddle: https://dotnetfiddle.net/VDzGao

Fetching data from JsonRespone array

{
"contentServiceInfo": {
"contentServiceId": 16199,
"siteId": 9814,
"containerInfo": {
"containerName": "credits",
"assetType": 2
}
},
The above code is Json respone from Server.I am using NewtonSoft for fetching the information.
Now I want to fetch the value of the containerName from Containerinfo object.
Can anyone provide me the solution.
I was trying the code below to fetch the details
foreach (JObject content in o.Children<JObject>())
{
foreach (JProperty prop in content.Properties())
{
//Console.WriteLine(prop.Name);
if (prop.Name == "containerName")
{
AccType = prop.Value.ToString();
}
}
}
But still I am not able to fetch the data
You can use dynamic object if you don't have a class.
string json = "{\"contentServiceInfo\":{\"contentServiceId\":16199,\"siteId\":9814,\"containerInfo\":{\"containerName\":\"credits\",\"assetType\":2}}}";
dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
var containerName = data.contentServiceInfo.containerInfo.containerName;
if json data is an array try following:
string json = "[{\"contentServiceInfo\":{\"contentServiceId\":16199,\"siteId\":9814,\"containerInfo\":{\"containerName\":\"credits\",\"assetType\":2}}}]";
dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
var containerName = data[0].contentServiceInfo.containerInfo.containerName;
Console.WriteLine(containerName);

Resources