How do you promote a JSON object into an array? - c#

Is it possible to "promote" a specific part of a json document into an array?
For example, the input would be something like:
{
"identifier": {"a":"awesome", "b":"back", "c":"cat"}
}
suppose I wanted to promote c, and in pseudocode, we'd do something like :
var payload = "{\"identifier\": {\"a\":\"awesome\", \"b\":\"back\", \"c\":\"cat\"}}";
var payloadWithPromotedC = Promoter.ToArray(payload, "identifier.c");
The result would be something like:
{
"identifier": {"a":"awesome", "b":"back", "c":["cat"]}
}
How do we declaritively promote a certain node inside of a json document into an array?

I'm not sure what you mean by "declaratively", but following your example you could do something like this using Json.Net:
public class Promoter
{
public static string ToArray(string json, string propertyPath)
{
JToken root = JToken.Parse(json);
JToken tokenToPromote = root.SelectToken(propertyPath);
if (tokenToPromote == null)
{
throw new JsonException(propertyPath + " was not found");
}
if (tokenToPromote.Type == JTokenType.Array)
{
return json; // value is already an array so return the original JSON
}
tokenToPromote.Replace(new JArray(tokenToPromote));
return root.ToString(Formatting.None);
}
}
Working demo: https://dotnetfiddle.net/bOUOWD

Related

Deserializing JSON with numbers as field using JsonSerializer

I need to deserialize this weird JSON (image below). I've seen some deserialization hints using Dictionary<>, etc. but the problem is that "parameters" contains different data, then previous keys.
Can I somehow get it to work using JsonSerializer deserializator without doing foreach loops and other suspicious implementations? I do need data from "data" in my application.
Here's some of my code:
using var client = new WebClient();
var json = client.DownloadString(GetJsonString());
var invoicesData = JsonSerializer.Deserialize<JsonMyData>(json, options);
If using Newtonsoft is necessary I might start using it.
With Newtonsoft you can parse and access arbitrary JSON documents, even ones that can't reasonably be deserialized into a .NET object. So something like:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace ConsoleApp35
{
class Program
{
static void Main(string[] args)
{
var json = #"
{
""myData"" :
{
""0"" : { ""data"": { ""A"":1,""B"":2} },
""1"" : { ""data"": { ""A"":1,""B"":2} },
""2"" : { ""data"": { ""A"":1,""B"":2} },
""3"" : { ""data"": { ""A"":1,""B"":2} },
""parameters"" : { ""p"":""a""}
},
""status"":{ }
}";
var foo = JObject.Parse(json);
var a = foo["myData"]["1"]["data"];
Console.WriteLine(a);
Console.WriteLine("Hit any key to continue");
Console.ReadKey();
}
}
}
I think you should really consider using Newtonsoft.Json instead of default JsonDeserializer, it is much easier to use in such situations.
If you are interested in processing this without foreach loops and wanting to access the data in a list format, I would suggest using Dictionary for this. When you use dictionary, you can use Objects as values that would compensate for differences in numbers (0, 1, 2, ..) and words (parameters).
// Classes to Deserialize data we need.
public class MyObject
{
[JsonProperty("data")]
public Data Data { get; set; }
}
public class Data
{
public int A { get; set; }
public int B { get; set; }
}
Usage in Main
// Read in the JSON
var myData = JsonConvert.DeserializeObject<dynamic>(jsonString)["myData"];
// Convert To Dictionary
Dictionary<string, dynamic> dataAsObjects = myData.ToObject<Dictionary<string, dynamic>>();
string searchFor = "3";
dataAsObjects.TryGetValue(searchFor, out dynamic obj);
if (obj != null)
{
// Conversion to int and matching against searchFor is to ensure its a number.
int.TryParse(searchFor, out int result);
if (result == 0 && result.ToString().Equals(searchFor))
{
MyObject myObject = obj.ToObject<MyObject>();
Console.WriteLine($"A:{myObject.Data.A} - B:{myObject.Data.B}");
}
else if (result == 8 && result.ToString().Equals(searchFor))
{
// I am not clear on whats your parameters class look like.
MyParameters myParams = obj.ToObject<MyParameters>();
}
}
Output
A:1 - B:2
With this method you can either access the numbers or the parameters element.

Adding a root element to json

I am generating a json string using NewtosoftJson using a table to format the the json. This is a simple key value pairs list and looks like:
public class items
{
private string key = String.Empty;
private string value = String.Empty;
public string Key
{
get
{
return key;
}
set
{
if (value != key)
{
key = value;
}
}
}
public string Value
{
get
{
return value;
}
set
{
if (value != this.value)
{
this.value = value;
}
}
}
}
When a list is populated and then serialised I get this JSON:
"Items": [
{
"Key":"FirstValue",
"Value":"One"
},
{
"Key":"SecondValue",
"Value":"Two"
},
{
"Key":"ThirdValue",
"Value":"Three"
}
]
What I need to get is:
"customData": {
"items": [
{
"Key":"FirstValue",
"Value":"One"
},
{
"Key":"SecondValue",
"Value":"Two"
},
{
"Key":"ThirdValue",
"Value":"Three"
}
]
}
I have tried creating a second class CustomData but can't see how to get the original JSON into the second class! Could you advice me on the correct way to construct the second class and method used to populate it please.
You can create an anonymous object and serialize that:
var objContainingItems = ... // your usual code
var customDataObj = new { customData = objContainingItems };
string json = JsonConvert.SerializeObject(customDataObj);
This is the most convenient solution if all you are interested in is serializing.
If you also want to be able to deserialize it, then you will need to use a class as specified in the answer by #William Moore.
Create a class customData and create a reference to the class items inside it. Then serialise your customData class using Newtonsoft.Json rather than your items class. So you will have:
public class CustomData
{
public items[] items; // I would rename the class items to item
}
Then you have an object of type customData, called customData which you pass into Newtonsoft.
You could then use the following to serialise/deserialise your data:
CustomData input = new CustomData();
input.items = []; // Whatever you have at the moment?
string json = JsonConvert.SerializeObject(account) //optionally set Formatting.Indented
CustomData deserialised = JsonConvert.DeserializeObject<CustomData>(json);

NewtonSoft json parsing

Can somebody help me to parse the json and get the details.
Lets say i have Top2 input parameter as Police and i want to know the respective Top3 and in that Top3 array i need to check whether Test1Child value is there or not.
I am using newtonsoft json + c# and so far i can get till DeviceTypes using below code
var json = File.ReadAllText(jsonFile); // below json is stored in file jsonFile
var jObject = JObject.Parse(json);
JArray MappingArray =(JArray)jObject["Top1"];
string strTop1 = ObjZone.Top1.ToString();
string strTop2 = ObjZone.Top2.ToString();
var JToken = MappingArray.Where(obj => obj["Top2"].Value<string>() == strTop2).ToList();
//Json
{
"Top1": [
{
"Top2": "Test1",
"Top3": [
"Test1Child"
]
},
{
"Top2": "Test2",
"Top3": [
"Test2Child"
]
}
]
}
I'd use http://json2csharp.com/ (or any other json to c# parser) and then use C# objects (it's just easier for me)
This would look like that for this case:
namespace jsonTests
{
public class DeviceTypeWithResponseTypeMapper
{
public string DeviceType { get; set; }
public List<string> ResponseTypes { get; set; }
}
public class RootObject
{
public List<DeviceTypeWithResponseTypeMapper> DeviceTypeWithResponseTypeMapper { get; set; }
}
}
and the use it like that in code:
var rootob = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(str);
var thoseThatHaveNotUsed = rootob.DeviceTypeWithResponseTypeMapper.Where(dtwrtm =>
dtwrtm.ResponseTypes.Any(rt => rt == "NotUsed"));
foreach (var one in thoseThatHaveNotUsed)
{
Console.WriteLine(one.DeviceType);
}
this code lists all the Device types that have "NotUsed" among the responses.
version 2 (extending your code) would look like that, i believe:
static void Main(string[] args)
{
var json = str; // below json is stored in file jsonFile
var jObject = JObject.Parse(json);
JArray ZoneMappingArray = (JArray)jObject["DeviceTypeWithResponseTypeMapper"];
string strDeviceType = "Police";
string strResponseType = "NotUsed";
var JToken = ZoneMappingArray.Where(obj => obj["DeviceType"].Value<string>() == strDeviceType).ToList();
var isrespTypeThere = JToken[0].Last().Values().Any(x => x.Value<string>() == strResponseType);
Console.WriteLine($"Does {strDeviceType} have response type with value {strResponseType}? {yesorno(isrespTypeThere)}");
}
private static object yesorno(bool isrespTypeThere)
{
if (isrespTypeThere)
{
return "yes!";
}
else
{
return "no :(";
}
}
result:
and if you'd like to list all devices that have response type equal to wanted you can use this code:
var allWithResponseType = ZoneMappingArray.Where(jt => jt.Last().Values().Any(x => x.Value<string>() == strResponseType));
foreach (var item in allWithResponseType)
{
Console.WriteLine(item["DeviceType"].Value<string>());
}

C# json object with empty json array

I have a returned json object that contains an empty json array
{[]}
EDIT:
How do I check this in an if statement?
string arrayData = string.Empty;
if (response.Contains("\"data\":"))
{
JToken root = JObject.Parse(response);
JToken data = root["data"];
if (data != null)
{
arrayData = data.ToString();
}
}
else
{
arrayData = response;
}
var responseDatas = JsonConvert.DeserializeObject<dynamic>(arrayData);
Here, responseDatas is now
{[]}
First, that is invalid JSON. The array should have a name, like this:
{ list: [] }
Second, you can deserialize the JSON using JSON.NET and then test the result:
public class ClassWithList
{
public List<object> list { get; set; }
}
var o = JsonConvert.DeserializeObject<ClassWithList>(json);
if (o.list != null && o.list.Count > 0)
{ }
The json is invalid (in the original question and in the accepted answer). You should include double quotes ...
{
"list": []
}

Templated serialization of C# objects to JSON

I need to serialize objects to JSON. I would like to do it with a template instead of using data annotations (as most frameworks do). Does anybody know a good way of doing this?
A picture says more than 1000 words. I'm looking for something that looks like this:
For example, if I had a class like this:
public class Test
{
public string Key { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public Test Related { get; set; }
}
And a had template string that could look like this:
{
id: "$Key",
name: "$Name",
related: "$Related.Name"
}
I want to get a JSON object, whose properties are filled in according to Key, Name and Related.Name of the object.
Basically I'm searching for a JSON serialization method that supports templating instead.
I don't know about any library that does this for you, but it's not that hard to build it yourself.
If you have your template, you need to parse it as JSON and then replace all of the placeholders with actual values. To do that, you can use the visitor pattern.
Since JSON.NET (the JSON library I'm using) doesn't seem to have a visitor, you can create one yourself:
abstract class JsonVisitor
{
public virtual JToken Visit(JToken token)
{
var clone = token.DeepClone();
return VisitInternal(clone);
}
protected virtual JToken VisitInternal(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
return VisitObject((JObject)token);
case JTokenType.Property:
return VisitProperty((JProperty)token);
case JTokenType.Array:
return VisitArray((JArray)token);
case JTokenType.String:
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.Date:
case JTokenType.Boolean:
case JTokenType.Null:
return VisitValue((JValue)token);
default:
throw new InvalidOperationException();
}
}
protected virtual JToken VisitObject(JObject obj)
{
foreach (var property in obj.Properties())
VisitInternal(property);
return obj;
}
protected virtual JToken VisitProperty(JProperty property)
{
VisitInternal(property.Value);
return property;
}
protected virtual JToken VisitArray(JArray array)
{
foreach (var item in array)
VisitInternal(item);
return array;
}
protected virtual JToken VisitValue(JValue value)
{
return value;
}
}
And then create a specialized visitor that replaces the placeholders with actual values:
class JsonTemplateVisitor : JsonVisitor
{
private readonly object m_data;
private JsonTemplateVisitor(object data)
{
m_data = data;
}
public static JToken Serialize(object data, string templateString)
{
return Serialize(
data, (JToken)JsonConvert.DeserializeObject(templateString));
}
public static JToken Serialize(object data, JToken template)
{
var visitor = new JsonTemplateVisitor(data);
return visitor.Visit(template);
}
protected override JToken VisitValue(JValue value)
{
if (value.Type == JTokenType.String)
{
var s = (string)value.Value;
if (s.StartsWith("$"))
{
string path = s.Substring(1);
var newValue = GetValue(m_data, path);
var newValueToken = new JValue(newValue);
value.Replace(newValueToken);
return newValueToken;
}
}
return value;
}
private static object GetValue(object data, string path)
{
var parts = path.Split('.');
foreach (var part in parts)
{
if (data == null)
break;
data = data.GetType()
.GetProperty(part)
.GetValue(data, null);
}
return data;
}
}
The usage is then simple. For example, with the following template:
{
id : "$Key",
name: "$Name",
additionalInfo:
{
related: [ "$Related.Name" ]
}
}
You can use code like this:
JsonTemplateVisitor.Serialize(data, templateString)
The result then looks like this:
{
"id": "someKey",
"name": "Isaac",
"additionalInfo": {
"related": [
"Arthur"
]
}
}
You might want to add some error-checking, but other than that, the code should work. Also, it uses reflection, so it might not be suitable if performance is important.
10 years have passed since I've posted the question. Since I've been working with Node.JS and discovered Handlebars and how it is pretty easy to get it to parse JSON instead of HTML template. The Handlebars project has been converted to .NET.
You can use a special ITextEncoder to let Handlebars generate JSON:
using HandlebarsDotNet;
using System.Text;
public class JsonTextEncoder : ITextEncoder
{
public void Encode(StringBuilder text, TextWriter target)
{
Encode(text.ToString(), target);
}
public void Encode(string text, TextWriter target)
{
if (text == null || text == "") return;
text = System.Web.HttpUtility.JavaScriptStringEncode(text);
target.Write(text);
}
public void Encode<T>(T text, TextWriter target) where T : IEnumerator<char>
{
var str = text?.ToString();
if (str == null) return;
Encode(str, target);
}
}
Let's see it in action:
using HandlebarsDotNet;
var handlebars = Handlebars.Create();
handlebars.Configuration.TextEncoder = new JsonTextEncoder();
var sourceTemplate = #"{
""id"": ""{{Key}}"",
""name"": ""{{Name}}"",
""related "": ""{{Related.Name}}""
}";
var template = handlebars.Compile(sourceTemplate);
var json = template(new
{
Key = "Alpha",
Name = "Beta",
Related = new
{
Name = "Gamme"
}
});
Console.WriteLine(json);
This will write the following:
{
"id": "Alpha",
"name": "Beta",
"related ": "Gamme"
}
I did a small write-up on the topic on my blog: Handlebars.Net & JSON templates. In this blog I also discuss how to improve debugging these templates.
You can also use a Text Template file for your json template . The template engine will fill in the blanks and return you the result.
If you are using Visual Studio,
Create a .tt file ,
Mark it with TextTemplatingFilePreprocessor in Custom Tool property of the file. This will create a new class for you that takes care of processing the template.
For integrating your data in the resulted string , extend the newly generated class in a separate file , in which you pass the data (the arbitrary class from you image).
Use this to get the json formatted code;
MyData data = ...;
MyTemplatePage page = new MyTemplatePage(data);
String pageContent = page.TransformText();
Now the pageContent have the json formatted string; For more details about how to handle the .tt file , look here : Text Template Control Blocks
I had exactly the same need. I needed an end user (technical users but not developers) to be able to create their own json files that can later be filled via data.
Microsoft Teams is doing something similar with their adaptive card website:
https://adaptivecards.io/designer/
On the bottom left there is a json "template" and on the bottom right a json to load into the template.
Conclusion: Despite extensive research I have not found any .NET library doing this.
Sorry (๑•́ㅿ•̀๑).
Screenshot of adaptive card designer

Categories

Resources