deserialize json into vb.net class with conditional definition - json

I am an intermediate programmer and am trying to find a solution to a JSON deserialize issue in vb.net. I'm working some product updates with an API (not mine) in which I have to get the product, deserialize it, change some values, and post an update.
I have built out a bunch of vb.net classes to handle the deserialize (newtonsoft.json) of a JSON doc and for the most part works great. However, I ran into a situation on some products where the object could be defined one of two ways depending on the value of a particular json object.
Here is a snippet of the JSON:
"PriceGrids": [
{
"IsBasePrice": false,
"PriceConfigurations": [
{
"Criteria": "Imprint Method",
"Value": [
"SILKSCREEN"
]
}
]
},
{
"IsBasePrice": true,
"PriceConfigurations": [
{
"Criteria": "Size",
"Value": [
{
"Attribute": "Length",
"Value": "25",
"Unit": "cm"
},
{
"Attribute": "Width",
"Value": "7.5",
"Unit": "cm"
},
{
"Attribute": "Height",
"Value": "14.5",
"Unit": "cm"
}
]
}
]
}
So Value has two definitions depending on the IsBasePrice value (true/false). I currently have Value defined in the class as below.
Private _Value As New List(Of String)
Public Property Value() As List(Of String)
Get
Return _Value
End Get
Set(value As List(Of String))
_Value = value
End Set
End Property
This, of course, fails when it isn't a list of string(s). I can't find an example anywhere of handling this. Is it even possible in vb.net to handle a conditional object definition based on the Json provided?

Related

Operator "missing" not working properly in JsonLogic

I am using JsonLogic to validate my input payload with the rules defined using JsonLogic. I am able to test the rules using "Play with it" tool and my rules work perfectly fine against my input data.
But when I run the same rules through my .net Core application by passing payload from Postman the rules always return the else condition even when it should the error from if condition.
{
"if": [
{
"missing": [
"ProposedProjectDetails.IsFreezone",
"ProposedProjectDetails.InterestedToLeaseFrom",
"ProposedProjectDetails.IndustryType",
"ProposedProjectDetails.OtherType",
"ProposedProjectDetails.ProjectDescription",
"ProposedProjectDetails.OutputofFacility",
"ProposedProjectDetails.ProductionCapacity",
"ProposedProjectDetails.ProductionCapacityVolume",
"ProposedProjectDetails.MainRawMaterials",
"ProposedProjectDetails.RawMaterialQuantity",
"ProposedProjectDetails.RawMaterialEstimatedCost",
"ProposedProjectDetails.RawMaterialEstimatedTotInvestment",
"ProposedProjectDetails.AnnualSalesRevenue",
"ProposedProjectDetails.ConstructionStartDate",
"ProposedProjectDetails.Skilledjobs",
"ProposedProjectDetails.TotalAccomodationRequired",
"ProposedProjectDetails.TotalWorkerSalary",
"ProposedProjectDetails.EBITDA",
"ProposedProjectDetails.PortCargoImports",
]
},
"Missing mandatory inputs",
"all good"
]
}
Sample input payload is
{
"companyId": "string",
"serviceCode": "IPA",
"serviceType": "string",
"serviceName": "string",
"crmApplicationId": "string",
"crmReferenceNumber": "string",
"portalReferenceNumber": "string",
"data": {
"proposedProjectDetails": {
"outputofFacility": 2,
"productionCapacity": 0,
"productionCapacityVolume": 0,
"others": "string",
"shiftsPerDay": 0
}
}
}
My .Net code which is evaluating this is
public ValidationResponse Validate(JObject validation, JObject data)
{
var evaluator = new JsonLogicEvaluator(EvaluateOperators.Default);
var result = evaluator.Apply(validation, data);
return new ValidationResponse
{
IsValid = (string.Equals("All Fine", result.ToString(), StringComparison.OrdinalIgnoreCase)),
Message = result
};
}
When I run above code with JsonRules and above Payload, I always get all good in the response. But since I am missing required data in payload, it should get the error Missing mandatory inputs which I do get in JsonLogic "Play with it" tool.
Try keeping the names as json mapping fields camel case once like below
"if": [
{
"missing": [
"proposedProjectDetails.isFreezone",
"proposedProjectDetails.interestedToLeaseFrom",
]
},
"Missing mandatory inputs",
"all good"
]
}

How to traverse list of nested maps in scala

I have been given a json string that looks like the following one:
{
"dataflows": [
{
"name": "test",
"sources": [
{
"name": "person_inputs",
"path": "/data/input/events/person/*",
"format": "JSON"
}
],
"transformations": [
{
"name": "validation",
"type": "validate_fields",
"params": {
"input": "person_inputs",
"validations": [
{
"field": "office",
"validations": [
"notEmpty"
]
},
{
"field": "age",
"validations": [
"notNull"
]
}
]
}
},
{
"name": "ok_with_date",
"type": "add_fields",
"params": {
"input": "validation_ok",
"addFields": [
{
"name": "dt",
"function": "current_timestamp"
}
]
}
}
],
"sinks": [
{
"input": "ok_with_date",
"name": "raw-ok",
"paths": [
"/data/output/events/person"
],
"format": "JSON",
"saveMode": "OVERWRITE"
},
{
"input": "validation_ko",
"name": "raw-ko",
"paths": [
"/data/output/discards/person"
],
"format": "JSON",
"saveMode": "OVERWRITE"
}
And I have been asked to use it as some kind of recipe for an ETL pipeline, i.e., the data must be extracted from the "path" specifid in the "sources" key, the transformations to be carried out are specified within the "transformations" key and, finally, the transformed data must saved to one of the two specified "sink" keys.
I have decided to convert the json string into a scala map, as follows:
val json = Source.fromFile("path/to/json")
//parse
val parsedJson = jsonStrToMap(json.mkString)
implicit val formats = org.json4s.DefaultFormats
val parsedJson = parse(jsonStr).extract[Map[String, Any]]
so, with that, I get a structure like this one:
which is a map whose first value is a list of maps. I can evaluate parsedJson("dataflows") to get:
which is a list, as expected, but, then I cannot traverse such list, even though I need to in order to get to the sources, transformations and sinks. I have tried using the index of the listto, for example, get its first element, like this: parsedJson("dataflows")(0), but to no avail.
Can anyone please help me traverse this structure? Any help would be much appreciated.
Cheers,
When you evaluate parsedJson("dataflows") a Tuple2 is returned aka a Tuple which has two elements that are accessed with ._1 and ._2
So for dataflows(1)._1 the value returned is "sources" and dataflows(1)._2 is list of maps (List[Map[K,V]) which can be traversed like you would normally traverse elements of a List where each element is Map
Let's deconstruct this for example:
val dataFlowsZero = ("sources", List(Map(42 -> "foo"), Map(42 -> "bar")))
The first element in the Tuple
scala> dataFlowsZero._1
String = sources
The second element in the Tuple
scala> dataFlowsZero._2
List[Map[Int, String]] = List(Map(42 -> foo), Map(42 -> bar))`
Map the keys in each Map in List to a new List
scala> dataFlowsZero._2.map(m => m.keys)
List[Iterable[Int]] = List(Set(42), Set(42))
Map the values in each Map in the List to a new List
scala> dataFlowsZero._2.map(m => m.values)
List[Iterable[String]] = List(Iterable(foo), Iterable(bar))
The best solution is to convert the JSON to the full data structure that you have been provided rather than just Map[String, Any]. This makes it trivial to pick out the data that you want. For example,
val dataFlows = parse(jsonStr).extract[DataFlows]
case class DataFlows(dataflows: List[DataFlow])
case class DataFlow(name: String, sources: List[Source], transformations: List[Transformation], sinks: List[Sink])
case class Source(name: String, path: String, format: String)
case class Transformation(name: String, `type`: String, params: List[Param])
case class Param(input: String, validations: List[Validation])
case class Validation(field: String, validations: List[String])
case class Sink(input: String, name: String, paths: List[String], format: String, saveMode: String)
The idea is to make the JSON handler do most of the work to create a type-safe version of the original data.

JSON serialization technicalities in VB.NET

What is the difference between the following serialization methods?
First Method
JsonConvert.SerializeObject(list or datatable)
and the output is
i.e. (3) [Object, Object, Object]
Second Method
Dim parent = Prtdata
Dim lGridColumns = New With {
Key .data = parent
}
Dim Setting = New JsonSerializerSettings
Setting.PreserveReferencesHandling = PreserveReferencesHandling.Objects
Dim jsonObject = JsonConvert.SerializeObject(lGridColumns, Formatting.Indented)
Return jsonObject
and its output is
{
"data": [
{
"RecID": 2383,
"PrtStatus": 0,
"PtFilenum": 15090248,
"PrtFilenum": 13090701,
"FullName": "asdasd",
"DOB": "04 Oct 1985"
},
{
"RecID": 3387,
"PrtStatus": 1,
"PtFilenum": 15090248,
"PrtFilenum": 15120996,
"FullName": "marwam mohmmad saleem",
"DOB": "24 May 2017"
},
{
"RecID": 3388,
"PrtStatus": 1,
"PtFilenum": 15090248,
"PrtFilenum": 170227111,
"FullName": "asd dsf as a",
"DOB": "27 Feb 2017"
}
]
}
why the output looks different in the browser console?
As the first comment, you can find a Serialization Guide on the website of NewtonSoft.json, in my answer I just provide a more elaborate version of my comment earlier.
The first scenario, where you are serializing something implemented IEnumerable (eg: list, array), will be represented by an array in Json, eg:
[{ "property": "value", "id": 0 }, {"property": "value", "id": 1}]
For the second scenario, you are doing several things differently, for example you are providing the PreserveReferencesHandling in the JsonSerializerSettings which would also preveserve any references made in the objects you are serializing, eg:
[{"$id": 1, "title": "item1"}, {"$id": 2, "title": "item2", "previous": { "$ref": 1 }]
This would make sure that when deserialized, the second object would contain a reference to the first object, inside the property previous.
Another thing you are doing differently is providing the Formatting.Indented, which will create a more reader friendly json document, having line breaks and indentation. The previous Json would then become something similar to this:
[{
"$id": 1,
"title": "item1"
},
{
"$id": 2,
"title": "item2",
"previous": {
"$ref": 1
}
}]
And, the last big difference is that in the last example, you are serializing a single object, cause it's public properties to be serialized, eg:
{
"data": [
...
]
}
Where data is a property on the object you are serializing.

How to get json data into apex salesforce?

I have a json like this, which i am getting in the response from http call
{
"offset": 0,
"limit": 50,
"objects": [
{
"id": "59118fb6e4b0168ec4b56692",
"modifiedDate": 1494323126886,
"requestedIds": null,
"mergedIds": [],
"properties": {
"name": [
{
"value": "Abhimanyu",
"metadata": {}
}
],
"company": [],
"title": [],
"email": [
{
"value": "absinghrathore127#gmail.com",
"metadata": {}
}
]
},
"state": "ACTIVE"
},
{
"id": "590d5813e4b03a8336fa1642",
"modifiedDate": 1494046739619,
"requestedIds": null,
"mergedIds": [],
"properties": {
"name": [
{
"value": "Tim Archer",
"metadata": {}
}
],
"company": [],
"title": [],
"email": [
{
"value": "tim#avocado.com",
"metadata": {}
}
]
},
"state": "ACTIVE"
}
],
"size": 2
}
and i am able to get objects from json via this following code :
String s = res.getBody();
Map<String,Object> jsonMap = (Map<String, Object>)JSON.deserializeUntyped(s);
String jsonSubset = JSON.serialize(jsonMap.get('objects'));
What i need is the value of name and email in some variable.
Please help me out in this!!
This is going to be a tedious task but once you've classified your all data into appropriate Wrapper classes then it's fairly simple and easy to maintain.
First thing is to define your MainWrapper class. This will contain all the at it's own level. If it has any Object as key-pair then we need to make sure to include it as a List<>. So This is how your MainWrapper should be:
public class MainWrapper {
Integer offset; // Singleton variable
Integer limits; // Singleton variable
List<ObjectsWrapper> objects; // Collection variable since it starts with [],
Integer size; // Singleton variable
}
Since you've array of objects in JSON that's why I've included it as a List in MainWrapper. Now it's time to define ObjectsWrapper. Below is wrapper defined for the same.
public class ObjectsWrapper {
String id;
String modifieddate;
String requestedIds;
PropertyWrapper properties;
}
Since there is only on properties associated with objects that's why it's a non-collection type. Below is representation of properties.
public class PropertyWrapper {
List<NameWrapper> name;
List<String> company;
List<String> title;
List<EmailWrapper> email;
String state;
}
public class NameWrapper {
String name;
String metadata;
}
I guess now you've a fair idea of how to organize data of JSON into various wrapper class. Once you're done with this, simply deserialize the JSON into MainWrapper class and access it.
For example:
MainWrapper mainJSONWrapper = (MainWrapper) JSON.deserialize(JSON,MainWrapper.class);
List<ObjectsWrapper> objectsLst = mainJSONWrapper.objects;
for(ObjectsWrapper obj:objectsLst) {
List<NameWrapper> lstNameWrapper = obj.properties;
for(NameWrapper nameObj:NameWrapper) {
System.debug('Name:'+nameObj.name);
System.debug('metadata:'+nameObj.metadata);
}
}
Above code is not tested but yes, it will give idea how you should deserialize JSON in appropriate manner.
Also go through this answer..How to deserialize a JSON String to Apex

Using JPath, how do I return a value based on another key value pair of the same node?

I have the following JSON file. It is truncated for brevity of this question. An actual JSON file would contain 10 - 20 messages and 3 - 15 results.
{
"messages": [
{
"type": "msgInfo",
"description": "Some stuff happened"
},
{
"type": "msgInfo",
"description": "More stuff happened"
},
{
"type": "msgInfo",
"description": "yup, more stuff happened"
}
],
"results": [
{
"parameterId": "val_1",
"dataType": "Double",
"value": 123.45
},
{
"parameterId": "val_2",
"dataType": "Double",
"value": 246.80
},
{
"parameterId": "val_3",
"dataType": "Double",
"value": 135.79
},
{
"parameterId": "val_4",
"dataType": "Long",
"value": 20161021
}
]
}
I'm trying to retrieve the value of the value key based on the value of the parameterId key. For example, I need to return "123.45" using a JPath to "val_1".
So far I have this code (copied from this post) but I can't get the path correct.
JObject obj = JObject.Parse(json);
JToken token = obj["results"]["parameterId"];
Console.WriteLine(token.Path + " -> " + token.ToString());
Console.ReadLine();
What do I need to do in order to return "123.45" using a JPath to "val_1"?
To get the value token from one of the results in the results array based on the value of the parameterId token, you need to use the SelectToken method with a JSONPath query expression:
JToken token = obj.SelectToken("$.results[?(#.parameterId=='val_1')].value");
JSONPath syntax can be a little tricky to get right sometimes, depending on what you're trying to do, so you might find an online expression evaluator such as this one helpful to experiment with.
Alternatively, you can use a LINQ query to do the same thing:
JToken token = obj["results"]
.Where(result => (string)result["parameterId"] == "val_1")
.Select(result => result["value"])
.FirstOrDefault();
Here is a fiddle showing both approaches: https://dotnetfiddle.net/8qiSCa