JSON response with varying nesting pattern from 3rd-party API - asp.net

I get a third-party API response which has several nested properties:
Variation 1:
{
"Property1": {
"Property2A": {
"Key1": "1",
"Key2": "2",
"Key3": "3"
},
"Property2B": {
"Property3": {
"Key4": "A",
"Key5": "B",
"Key6": "C",
"Property4": {
"Property5": {
"Property6": [
{
"Key7": "1",
"Property7A": {
"X": "1"
},
"Property7B": {
"X": "2"
},
"Property7C": {
"X": "3"
}
},
{
"Property7D": {
"X": "INeedThisString"
}
}
]
}
}
}
}
}
}
I only need the value "INeedThisString".
I am able to reach the value of property "X": "INeedThisString" by a suitable model structure (generated by mapping the model over the Json-File) and with the following declarations:
Rootobject obj = JsonConvert.DeserializeObject<Rootobject>(MyJsonString);
string result = obj.Property1.Property2B.Property3.Property4.Property5.Property6[1].Property7D.X;
Here's my problem:
The API sometimes issues a variation of this architecture which has -as the only difference- Property3 declared as an array such as:
Variation 2:
{
"Property1": {
"Property2A": {
"Key1": "1",
"Key2": "2",
"Key3": "3"
},
"Property2B": {
"Property3": [ //<-----
{
"Key4": "A",
"Key5": "B",
"Key6": "C",
"Property4": {
"Property5": {
"Property6": [
{
"Key7": "1",
"Property7A": {
"X": "1"
},
"Property7B": {
"X": "2"
},
"Property7C": {
"X": "3"
}
},
{
"Property7D": {
"X": "INeedThisString"
}
}
]
}
}
}
] //<-----
}
}
}
//<-----: added 2x for illustration purposes.
Obviously, variation 1 and my current model structure do not declare Property3 as an array.
Question:
What's an elegant approach to solve this without touching the Json (deletions/replacements) in pre-processing?
Should I implement an alternative set of models and switch between those two model-sets via an error-function? Please note that the keys within properties 7A-7D are all the same: "X".

My current working solution covers an alternative set of classes needed for parsing, resulting from mapping Variant 2 of the Json response.
All classes are decorated by Json headers [JsonProperty("actual-string-shown-in-Json")] via using directive using Newtonsoft.Json.
The two alternative JsonConvert functions are placed within a try-catch statement:
string result;
try
{
Rootobject obj = JsonConvert.DeserializeObject<Rootobject>(MyJsonString);
result = obj.Property1.Property2B.Property3.Property4.Property5.Property6[1].Property7D.X;
}
catch
{
AltRootobject obj = JsonConvert.DeserializeObject<AltRootobject>(MyJsonString);
result = obj.AltProperty1.AltProperty.AltProperty[0].AltProperty.AltProperty5.AltProperty6[1].AltProperty7D.AltX;
}

Related

DynamoDB New Image has default Message and Id values

I noticed the NEW IMAGE in dynamodb stream always has these two fields:
"Message":"New item!"
"Id":"101"
These values are always there UNLESS my record itself has a Id and a Message field, in which case the default values are overridden. But since my table doesn't have "Id" and "Message" attributes, these values are ALWAYS present. Is this expected behavior?
I created sample dynamodb stream record:
Records: [
{
eventID: "1",
eventVersion: "1.0",
dynamodb: {
Keys: {
id: {
N: "101"
}
},
NewImage: {
Data: {
S: "New data!"
},
id: {
N: "99"
}
},
OldImage: {
Data: {
S: "Old data!"
},
id: {
N: "99"
}
},
StreamViewType: "NEW_AND_OLD_IMAGES",
SequenceNumber: "111",
SizeBytes: 26
},
awsRegion: "xyz",
eventName: "INSERT",
eventSourceARN: "xyz",
eventSource: "aws:dynamodb"
}
]
When unmarshalling record.dynamodb.NewImage, I get a NewImage as follows:
"NewImage":{"Message":"New item!", "Id":101, "Data":"New data!", "id":99}

Is it possible to deserialize inline arrays with Json.NET?

Let me try to simplify my question by extracting and abstracting relevant information from a complex object and large JSON text.
Class Foo()
{
string Name;
Version[] versions;
}
JsonText(converted from XML) can be something like this:
"Foo": {
"Name": "test",
"versions": {
"Major": "1",
"Minor": "1"
},
"versions": {
"Major": "2",
"Minor": "1"
},
"versions": {
"Major": "3",
"Minor": "1"
}
}
or the following:
"Foo": {
"Name": "test",
"versions": {
"Major": "1",
"Minor": "1"
}
}
JsonConvert.DeserializeObject(JsonText) results in:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'Version[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
Is it possible to do this desrialization?
Updated based on commment
Your Json doesn't look valid. A Json needs to be enclosed with "[" "]". For example,
[ "Ford", "BMW", "Fiat" ]
If you are allowed to modify your Json, You could modify the Json as following
{
"Foo":
{
"Name": "test",
"versions": [
{
"Major": "1",
"Minor": "1"
},
{
"Major": "2",
"Minor": "1"
},
{
"Major": "3",
"Minor": "1"
}]
}
}
Based on your comment, you would like to avoid the Json Array Syntax if there is only a single element. On other cases, you would like to stick to Array Syntax. In such scenario,you could write a Custom Json Converter.
class CustomConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>().ToArray();
}
return new List<T> { token.ToObject<T>() }.ToArray();
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can decorate your class with JsonConverter attribute to use the Custom Json Converter.
class Wrapper
{
public Foo Foo{get;set;}
}
class Foo
{
[JsonProperty("Name")]
public string Name{get;set;}
[JsonProperty("versions")]
[JsonConverter(typeof(CustomConverter<Version>))]
public Version[] versions{get;set;}
}
Now you can deserialize as
var result =JsonConvert.DeserializeObject<Wrapper>(str);
This would now work, if you use either of following Json definition.
{
'Foo':
{
'Name': 'test',
'versions': [
{
'Major': '1',
'Minor': '1'
},
{
'Major': '2',
'Minor': '1'
},
{
'Major': '3',
'Minor': '1'
}]
}
}
Single Version (Without Array syntax)
{
'Foo':
{
'Name': 'test',
'versions':
{
'Major': '1',
'Minor': '1'
}
}
}

Querying Cosmos Nested JSON documents

I would like to turn this resultset
[
{
"Document": {
"JsonData": "{\"key\":\"value1\"}"
}
},
{
"Document": {
"JsonData": "{\"key\":\"value2\"}"
}
}
]
into this
[
{
"key": "value1"
},
{
"key": "value2"
}
]
I can get close by using a query like
select value c.Document.JsonData from c
however, I end up with
[
"{\"key\":\"value1\"}",
"{\"key\":\"value2\"}"
]
How can I cast each value to an individual JSON fragment using the SQL API?
As David Makogon said above, we need to transform such data within our app. We can do as below:
string data = "[{\"key\":\"value1\"},{\"key\":\"value2\"}]";
List<Object> t = JsonConvert.DeserializeObject<List<Object>>(data);
string jsonData = JsonConvert.SerializeObject(t);
Screenshot of result:

API Gateway and DynamoDB PutItem for String Set

I can't seem to find how to correctly call PutItem for a StringSet in DynamoDB through API Gateway. If I call it like I would for a List of Maps, then I get objects returned. Example data is below.
{
"eventId": "Lorem",
"eventName": "Lorem",
"companies": [
{
"companyId": "Lorem",
"companyName": "Lorem"
}
],
"eventTags": [
"Lorem",
"Lorem"
]
}
And my example template call for companies:
"companies" : {
"L": [
#foreach($elem in $inputRoot.companies) {
"M": {
"companyId": {
"S": "$elem.companyId"
},
"companyName": {
"S": "$elem.companyName"
}
}
} #if($foreach.hasNext),#end
#end
]
}
I've tried to call it with String Set listed, but it errors out still and tells me that "Start of structure or map found where not expected" or that serialization failed.
"eventTags" : {
"SS": [
#foreach($elem in $inputRoot.eventTags) {
"S":"$elem"
} #if($foreach.hasNext),#end
#end
]
}
What is the proper way to call PutItem for converting an array of strings to a String Set?
If you are using JavaScript AWS SDK, you can use document client API (docClient.createSet) to store the SET data type.
docClient.createSet - converts the array into SET data type
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName:table,
Item:{
"yearkey": year,
"title": title
"product" : docClient.createSet(['milk','veg'])
}
};

Get a tree like structure out of path string

I am stuck since 2 days, as I am not to firm with pointers and recursion. I have an array of path like structures, lets say:
s:=[]string {
"a/b/c",
"a/b/g",
"a/d",
}
With a data structure like this:
type Node struct {
Name string `json:"name"`
Children []Node `json:"children"`
}
I would like to end up with something like this:
{
"name": "a",
"children": [
{
"name": "b",
"children": [
{
"name": "c",
"children": []
},
{
"name": "g",
"children": []
}
]
},
{
"name": "d",
"children": []
}
]
}
I tried to build it with a recursion, which works kind of fine, but only for one string (e.g. "a/b/c"), as soon as I try to implement something which should add missing nodes ("g" in "a/b/g") to a tree I am stuck.
I had something like:
func appendChild(root Node, children []string) Node {
if len(children) == 1 {
return Node{children[0], nil}
} else {
t := root
t.Name=children[0]
t.Children = append(t.Children, appendChild(root, children[1:]))
return t
}
}
Could someone point me to an efficient solution?
https://play.golang.org/p/9pER5cwChF
func AddToTree(root []Node, names []string) []Node {
if len(names) > 0 {
var i int
for i = 0; i < len(root); i++ {
if root[i].Name == names[0] { //already in tree
break
}
}
if i == len(root) {
root = append(root, Node{Name: names[0]})
}
root[i].Children = AddToTree(root[i].Children, names[1:])
}
return root
}
Example output (note that I used omitempty on the children field, because I don't like null entries in my JSONs):
[{
"name": "a",
"children": [{
"name": "b",
"children": [{
"name": "c"
}, {
"name": "g"
}]
}, {
"name": "d"
}]
}]
Notable difference from your version:
It operates on a list of nodes instead of the children of a single node. This is important, as your version assumes that all of the trees have the same single root node (a), when this might not be the case. The only way to handle that in your version is to have a "fake" node at the root.
It does NOT reuse the input node. This is one of the primary problems with your code. If len(children) > 1, you update the input node's name, append to it's children, then recurse. This means that every prior level of the slice becomes part of the children. You need to create a new node instead.
It actually searches the tree. You're not searching the tree to see if the item being inserted already exists, so you duplicate nodes (specifically, node b)

Resources