json C# 7 Tuple Support - json

I want to get my C#7 tuple property names in my JSON (Newtonsoft.Json) output.
My problem is:
When I want to convert my tuple to JSON format that not support my parameters names.
For example this is my "Test2" method and you can see the JSON output:
public void Test2()
{
var data = GetMe2("ok");
var jsondata = JsonConvert.SerializeObject(data);//JSON output is {"Item1":5,"Item2":"ok ali"}
}
public (int MyValue, string Name) GetMe2(string name)
{
return (5, name + " ali");
}
The JSON output is "{"Item1":5,"Item2":"ok ali"}" but i want "{"MyValue":5,"Name":"ok ali"}";
This is not impossible because I can get property names in runtime:
foreach (var item in this.GetType().GetMethods())
{
dynamic attribs = item.ReturnTypeCustomAttributes;
if (attribs.CustomAttributes != null && attribs.CustomAttributes.Count > 0)
{
foreach (var at in attribs.CustomAttributes)
{
if (at is System.Reflection.CustomAttributeData)
{
var ng = ((System.Reflection.CustomAttributeData)at).ConstructorArguments;
foreach (var ca in ng)
{
foreach (var val in (IEnumerable<System.Reflection.CustomAttributeTypedArgument>)ca.Value)
{
var PropertyNameName = val.Value;
Console.WriteLine(PropertyNameName);//here is property names of C#7 tuple
}
}
}
}
dynamic data = attribs.CustomAttributes[0];
var data2 = data.ConstructorArguments;
}
}

For the specific case here, it is impossible. That's because SerializeObject has no way of finding out where the tuple came from, all it sees is ValueTuple<int, string>.
The situation would be different if you were serializing an object with tuple properties, in which case SerializeObject could use reflection to find the TupleElementNames attributes (even though it currently doesn't).

The short answer it that tuples don't have properties.
A tuple is a bag of values used, mainly, to return multiple values from a method.
They were never intended to model entities.
The only way to solve your problem, if you don't want to create a type for that, is:
public void Test2()
{
var data = GetMe2("ok");
var jsondata = JsonConvert.SerializeObject(new { data.MyValue, data.Name });//JSON output is {"Item1":5,"Item2":"ok ali"}
}

Related

How to extract values from a JsonResult?

How to extract values from a JsonResult, possibly as a list of Name-Value pairs ?
Coding on ASP.Net Core 3.1
Here is my code from Controller in short:
JsonResult jsonStr = CalcPrint();
...
...
public JsonResult CalcPrint()
{
int usuage;
double bAmt
...
...
var calcResult = new { usuage, bamt = bAmt };
return Json(calcResult);
}
Debugging shows jsonStr.Value as follows:
{ usuage = 1200, bamt = 1875 }
I can assign this value to a string var and then split it; but not the right way i think.
Any other ways to get it possibly as a list ?

Can't iterate through JSON object

I'm pulling a JSON request from the politifact API:
request('http://politifact.com/api/v/2/statement/?format=[JSON]&order_by=-ruling_date&limit=1')
.then(({
data
}) => {
newArticles = extractListingsFromJSON(data);
and parsing it with a function that the JSON is passed to
function extractListingsFromJSON(json) {
var jsonObject = json.objects
Outputs entire objects array
var headline = jsonObject[0].facebook_headline
console.log("headline:\n" + headline)
Outputs headline from Objects[0]
This works as intended. However, when I try to iterate through the objects array like so:
for (var attr in jsonObject) {
console.log(attr+": "+jsonObject.facebook_headline);
}
Outputs "0: undefined"
I also tried:
console.log(attr+": "+jsonObject[facebook_headline];
Outputs nothing
As you mentioned yourself jsonObject is an array.
json.objects.forEach(function (i) {
console.log(i.facebook_headline)
})
You still need the attr key to iterate through the jsonObject. Try doing attr+" :"+jsonObject[attr].facebook_headline
instead.

JSONPath expression to get a value from an array on condition or just the first value

Given JSON structured like this:
{
"name":"Some Guy",
"emails":[
{
"description":"primary",
"status":"UNVERIFIED",
"email":"first#first-email.com"
},
{
"description":"home",
"status":"VERIFIED",
"email":"second#second-email.com"
},
{
"description":"away",
"status":"VERIFIED",
"email":"third#third-email.com"
}
]
}
I would like a JSONPath expression to get the first email with status VERIFIED and if there are none, then just get the first email in the array. So, given the example above, the result would be second#second-email.com. Given this example:
{
"name":"Some Guy",
"emails":[
{
"description":"primary",
"status":"UNVERIFIED",
"email":"first#first-email.com"
},
{
"description":"home",
"status":"UNVERIFIED",
"email":"second#second-email.com"
}
]
}
the result would be first#first-email.com.
Is this possible with a JSONPath expression?
You effectively have 2 JSONPath expressions, where the second one (first email) should be evaluated only if the first one (first verified email) returns nothing, so I don't think you can evaluate them both at the same time, in a single expression.
You can apply them one after the other, though:
public static String getEmail(String json) {
Configuration cf = Configuration.builder().options(Option.SUPPRESS_EXCEPTIONS).build();
DocumentContext ctx = JsonPath.using(cf).parse(json);
List<String> emails = ctx.read("$.emails[?(#.status == 'VERIFIED')].email");
if (!emails.isEmpty()) {
return emails.get(0);
}
return ctx.read("$.emails[0].email");
}
If the email array is empty, ctx.read("$.emails[0].email") will return null instead of throwing an exception, thanks to the option SUPPRESS_EXCEPTIONS.
If you don't know the number of paths in advance:
public static String getEmail(String json, String[] paths) {
Configuration cf = Configuration.builder().options(Option.ALWAYS_RETURN_LIST, Option.SUPPRESS_EXCEPTIONS).build();
DocumentContext ctx = JsonPath.using(cf).parse(json);
for (String path : paths) {
List<String> emails = ctx.read(path);
if (!emails.isEmpty()) {
return emails.get(0);
}
}
return null;
}
The option ALWAYS_RETURN_LIST means the return type is a list, even when you have one or zero results.
This code should work perfectly for you
//Use the json parsing library to extract the data
JsonPath jp = new JsonPath(json);
Map<String, Object> location = jp.get("name.emails[0]");
System.out.println(location);

How to get object attribute with weird name

I have a situation where the name of an attribute is xml namespace::element (e.g. http://giggle.com/some/Path::Element). It is weird situation because I can read it, but I can't access it.
Here is an example:
var name: String = "http://giggle.com/some/Path::Element";
var obj: Object = createAttributes(name);
// Now we have obj:{ "http://giggle.com/some/Path::Element" : [an array] }
for (var attr : Object in obj[name]){
// do something
}
This is just a simplification of the real thing. It seems like I can't read it neither with obj.name, nor obj[name], nor obj['"' + name + '"'].
I have even tried something like this (with different types for elem):
for (var elem: Object in obj) { // I tried Object, Array and * here
for (var c: Object in elem)
{
// do something with c
}
}
Why is this happening and how to overcome this issue? Since it is interesting problem, please don't consider changing the way I create obj. AS3 allows creation of such attribute and I am wandering how we are suppose to use it.
i guess Object's key is what that you named "weird name".
key could be name or index id (numbers in string format "1", "2",..)
for (var key:String in obj) {
var value:* = obj[key];
}

Create an instance of an object from a String/Symbol without the Class in Dart?

I know that it is possible to create an instance from a symbol like is shown in this link:
Create an instance of an object from a String in Dart?
But this doesn't work for me since what I want to do is create an instance without having the class.
This problem is caused because I have One class with an internal List:
class MyNestedClass {
String name;
}
class MyClass {
int i, j;
String greeting;
List<MyNestedClass> myNestedClassList;
}
And I want to convert a map to this class:
{
"greeting": "hello, there",
"i": 3,
"j": 5,
"myNestedClassList": [
{
"name": "someName1"
},{
"name": "someName2"
}
]
}
right now I am doing something like this:
static void jsonToObject(String jsonString, Object object) {
Map jsonMap = JSON.decode(jsonString); //Convert the String to a map
mapToObject(jsonMap, object); //Convert the map to a Object
}
static void mapToObject(Map jsonMap, Object object) {
InstanceMirror im = reflect(object); //get the InstanceMirror of the object
ClassMirror cm = im.type; //get the classMirror of the object
jsonMap.forEach((fieldNameStr, fieldValue) { // For each element in the jsonMap
var fieldName = new Symbol(fieldNameStr); // convert the fieldName in the Map to String
if (isPrimitive(fieldValue)) { // if fieldValue is primitive (num, string, or bool
im.setField(fieldName, fieldValue); //set the value of the field using InstanceMirror
} else if (fieldValue is List) { // else if the fieldValue is a list
ClassMirror listCm = (cm.declarations[fieldName] as VariableMirror).type; //get the class mirror of the list
var listReflectee = listCm.newInstance(const Symbol(''), []).reflectee; //create an instance of the field
for(var element in fieldValue) { //for each element in the list
if(!isPrimitive(element)) { // if the element in the list is a map (i.e not num, string or bool)
var listType = listCm.typeArguments[0]; //get the TypeMirror of the list (i.e MyNestedClass from List<MyNestedClass>)
//This is the line that doesn't work correctly
//It should be something like:
//
// ClassMirror.fromSymbol(listType.simpleName).newInstance(const Symbol(''), []);
//
var listObject = (listType as ClassMirror).newInstance(const Symbol(''), []); //create an instance of the specified listType
mapToObject(element, listObject); //convert the element to Object
}
listReflectee.add(element); //add the element to the list
};
} else { //else (the field value is a map
ClassMirror fieldCm = (cm.declarations[fieldName] as VariableMirror).type; // get the field ClassMirror from the parent declarations
var reflectee = fieldCm.newInstance(const Symbol(''), []).reflectee; //create an instance of the field
mapToObject(fieldValue, reflectee); // convert the fieldValue, which is a map, to an object
im.setField(fieldName, reflectee); // set the value of the object previously converted to the corresponding field
}
});
}
As you can see the lines that are not actually working are:
var listType = listCm.typeArguments[0]; //get the TypeMirror of the list (i.e MyNestedClass from List<MyNestedClass>)
var listObject = (listType as ClassMirror).newInstance(const Symbol(''), []); //create an instance of the specified listType
since they are creating an instance on localClassMirror and not on MyNestedClass. I'm looking for a method similar to:
ClassMirror.fromSymbol(listType.simpleName).newInstance(const Symbol(''), []);
you can see the full source code in the next URL:
DSON Source Code
If you have the full qualified name of the class you should be able to find the type using libraryMirror and then it should be similar to your linked question to create an instance.
I haven't done this myself yet and have not code at hand.
see also: how to invoke class form dart library string or file
An alternative approach would be to create a map at application initialization time where you register the supported types with their name or an id and look up the type in this map (this is like it's done in Go)