JSON.Net, AnonymousTypes and Dashes [duplicate] - c#

This question already has answers here:
How can I parse a JSON string that would cause illegal C# identifiers?
(3 answers)
Can you have a property name containing a dash
(3 answers)
Closed 7 years ago.
In C#, a valid variable name cannot contain dashes. But in Json, all property names are based off of strings, so what would be considered an invalid character for a C# variable name, could be considered valid in Json.
My question is, how does JSON.Net handle having a Dash, or other invalid data inside of a Property name when attempting to deserialize to an Anonymous Type, and more importantly, what do you replace invalid characters with in the Anonymous Type to capture it.
If example data is required, i can provide it but quite frankly just add a Dash (-) to a Json Property name, and you've got my situation in a nutshell.
P.S: I cannot change the Json itself, because it is being consumed from an API.

You can use a ContractResolver to manipulate how JSON.Net maps C# property names to JSON names.
For your example this code does that:
class DashContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
// Count capital letters
int upperCount = propertyName.Skip(1).Count(x => char.IsUpper(x));
// Create character array for new name
char[] newName = new char[propertyName.Length + upperCount];
// Copy over the first character
newName[0] = char.ToLowerInvariant(propertyName[0]);
// Fill the character, and an extra dash for every upper letter
int iChar = 1;
for (int iProperty = 1; iProperty < propertyName.Length; iProperty++)
{
if (char.IsUpper(propertyName[iProperty]))
{
// Insert dash and then lower-cased char
newName[iChar++] = '-';
newName[iChar++] = char.ToLowerInvariant(propertyName[iProperty]);
}
else
newName[iChar++] = propertyName[iProperty];
}
return new string(newName, 0, iChar);
}
}
class Program
{
static void Main(string[] args)
{
string json = #"{""text-example"":""hello""}";
var anonymous = new { textExample = "" };
var obj = JsonConvert.DeserializeAnonymousType(json, anonymous,
new JsonSerializerSettings
{
ContractResolver = new DashContractResolver()
});
}
}
It converts UpperCamelCase and lowerCamelCase to lower-dash-case. Therefore mapping to your JSON input.
This overload of DeserializeAnonymousType has not always been available, and isn't available in the version released with Visual Studio 2013. The current (stable) NuGet package has this overload in it.

I'd suggest looking at the Dynamic rather than Anonymous UI for Json.Net, which can deserialise your data to ExpandoObject, which is a dynamic type that behaves like a dictionary - i.e. similar to a JavaScript object. This will then mean the range of allowed property names goes up, because they become dictionary keys rather than .Net properties.
Kind of like: Deserialize a property as an ExpandoObject using JSON.NET

Related

How to convert a JSON array to a C# object? [duplicate]

This question already has answers here:
Deserialize JSON to C# Classes
(4 answers)
Convert json to a C# array?
(5 answers)
Closed 10 months ago.
I have a JSON array like this one (the number of elements is not static, there may be also only 2 elements or even 10):
{
"ids": [1, 2, 3, 4, 5]
}
How can i make it look like this in my C# application:
new object[]{ 1, 2 },
I'm a beginner to C# so I'm sorry if this is a simple question, but I could not find a matching answer.
Thank you
You should define a type to store your data:
public class MyData
{
[JsonProperty("ids")] // not strictly necessary*
public int[] Ids { get; set; }
}
Using the JSON.NET NuGet package:
string json = "{\"ids\":[1,2,3,4,5]}";
MyData result = JsonConvert.DeserializeObject<MyData>(json);
int[] ids = result.Ids;
Using the System.Text.Json NuGet package:
string json = "{\"ids\":[1,2,3,4,5]}";
MyData result = JsonSerializer.Deserialize<MyData>(json);
int[] ids = result.Ids;
* The JsonProperty attribute exists in both packages. It's not strictly necessary for your code as they should both handle case sensitivity issues by default, but it's useful to know about for situations where you don't necessarily have the same C# property names as JSON names.

Newtonsoft JSON JsonReaderException when trying to deserialize multidimensional array

I have a script in my application that gathers some JSON data from my PHP REST API. The API uses json_encode() to serialize a multidimensional array that it has generated. The script within my app should deserialize this into the equivalent in C# so that I can loop through it as each sub-array represents a "row" of information from the database. However, I am presented with this error:
Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: A. Path '', line 0, position 0.
at Newtonsoft.Json.JsonTextReader.ParseValue () [0x002b3] in <2073514815234917a5e8f91b0b239405>:0
at Newtonsoft.Json.JsonTextReader.Read () [0x0004c] in <2073514815234917a5e8f91b0b239405>:0
at Newtonsoft.Json.JsonReader.ReadAndMoveToContent () [0x00000] in <2073514815234917a5e8f91b0b239405>:0
at Newtonsoft.Json.JsonReader.ReadForType (Newtonsoft.Json.Serialization.JsonContract contract, System.Boolean hasConverter) [0x0004a] in <2073514815234917a5e8f91b0b239405>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <2073514815234917a5e8f91b0b239405>:0
at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x
The JSON (exactly as my app receives it from the server)
[{"type":"0","ID":"1","groupID":"0","authorID":"14","contents":"rfgwgfgdfssd","time_stamp":"0-0-0"}, {"type":"0","ID":"2","groupID":"0","authorID":"14","contents":"whwrgthwr","time_stamp":"0-0-0"}]
The Script
async void getUpdatesAsync()
{
Console.WriteLine("starting...");
BackendConnect connectionHandler = new BackendConnect("vocal/posts/index.php");
var updatesRequestInfo = new Dictionary<string, string>
{
{"sessionID", "c1f7a973c04e18a05342ecc2d16f7339"},
{"type", "updateFeed"},
{"userID", "14"}
};
connectionHandler.addData(updatesRequestInfo);
string updatesReturn = await connectionHandler.sendForm(); // returns the json
Console.WriteLine(updatesReturn); // the above json was copied and pasted from this output
List<Dictionary<string, string>> updatesArray = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(updatesReturn); // this line causes error
Console.WriteLine(updatesArray);
//updatesArray.ForEach(Console.WriteLine);
}
Another script in my app that also uses JsonConvert.DeserializeObject<DataType>(stringToDeserialize) works when a single dimensional array (["SUCCESS", "adlhfbaf"]) is given to the deserialize function and is able to parse this into a simple list, but fails when trying with the multidimensional array JSON above.
So far I have tried setting the updatesArray variable to an endless amount of different data types to see if that was the problem which I suspect it is, but as a newbie to languages like C# where you have to pre-define datatypes, I'm having a very hard time solving this one.
Essentially, I would like to import it as a list of dictionaries in C# so that I can use foreach() to loop through each index of the initial list and recover a dictionary where I can grab specific values (such as contents). Below is an example of what i'm looking for it to import (incorrect syntax I know but a rough idea at least)
var updatesArray = new List<Dictionary<string, string>>
{
{
{"type", "0"},
{"ID", "1"},
{"groupID", "0"},
{"authorID", "14"},
{"contents", "rfgwgfgdfssd"},
{"time_stamp", "0-0-0"}
},
{
{"type", "0"},
{"ID", "2"},
{"groupID", "0"},
{"authorID", "14"},
{"contents", "whwrgthwr"},
{"time_stamp", "0-0-0"}
}
};
Instead of deserializing into a dictionary. Create a model for your response. Then deserialize it to a List of that model. You can see an example of this below. I based it on the code you supplied just replaced all of the call outs with a variable that holds a JSON string as you would get from your callout
public static void Main()
{
{
//json string
//you should fix the timestamp coming back...not a real timestamp
var testresponse = "[{\"type\":\"0\",\"ID\":\"1\",\"groupID\":\"0\",\"authorID\":\"14\",\"contents\":\"rfgwgfgdfssd\",\"time_stamp\":\"0-0-0\"},{\"type\":\"0\",\"ID\":\"2\",\"groupID\":\"0\",\"authorID\":\"14\",\"contents\":\"whwrgthwr\",\"time_stamp\":\"0-0-0\"}]";
Console.WriteLine(testresponse);
var updatesArray = JsonConvert.DeserializeObject<List<TestModel>>(testresponse);
Console.WriteLine(updatesArray[0].Id);
Console.WriteLine(updatesArray[1].Id);
var after = JsonConvert.SerializeObject(updatesArray);
Console.WriteLine(after);
}
}
If the timestamp is a DateTime set the data type to DateTime but you will need to send a real DateTime from the server
public class TestModel
{
[JsonProperty(PropertyName = "type")]
public int Type { get; set; }
[JsonProperty(PropertyName = "ID")]
public int Id {get; set;}
[JsonProperty(PropertyName = "groupID")]
public int GroupId {get; set;}
[JsonProperty(PropertyName = "authorID")]
public int AuthorId {get; set;}
[JsonProperty(PropertyName = "contents")]
public string Contents {get; set;}
[JsonProperty(PropertyName = "time_stamp")]
public string TimeStamp {get; set;}
}
My Issue
Thank you so much to everyone who tried to solve my issue. Feeling pretty stupid with myself, I have now solved the issue xD. Big thanks to #Jason who mentioned viewing my variables contents in Hexadecimal. I wrote a few lines to convert the variable to Hex, copied this output and threw it into an online Hex to ASCII converter to find that I was misreading the output from my program and had ignored over double the amount of data was being returned by the PHP script due to a rogue print_r() command left within the PHP from testing which outputted the array before it was JSON encoded:
Array
(
[0] => Array
(
[type] => 0
[ID] => 1
[groupID] => 0
[authorID] => 14
[contents] => rfgwgfgdfssd
[time_stamp] => 0-0-0
)
[1] => Array
(
[type] => 0
[ID] => 2
[groupID] => 0
[authorID] => 14
[contents] => whwrgthwr
[time_stamp] => 0-0-0
)
)
[{"type":"0","ID":"1","groupID":"0","authorID":"14","contents":"rfgwgfgdfssd","time_stamp":"0-0-0"},{"type":"0","ID":"2","groupID":"0","authorID":"14","contents":"whwrgthwr","time_stamp":"0-0-0"}]
Instead of just:
[{"type":"0","ID":"1","groupID":"0","authorID":"14","contents":"rfgwgfgdfssd","time_stamp":"0-0-0"},{"type":"0","ID":"2","groupID":"0","authorID":"14","contents":"whwrgthwr","time_stamp":"0-0-0"}]
Other Useful Possible Issues Summarised
In order to assist others in the future, I see it being only fair for me to also mention some of the things I have learned from this thread COULD have been the problem with that error.
Rogue Byte Order Marks / Zero Width Spaces
In C#, Unicode Characters like these can be removed with the String.Trim() function
Zero Width Space is always represented in unicode as U+200B
Byte Order Marks change based on what encoding has been used. For UTF-16, the BOM character is represented as U+FEFF
This means the fix for this problem is myString = myString.Trim(new char[]{'\uFEFF','\u200B'});
Incorrect Datatype / Not Sure What Datatype To Use
Create your own object that fits the structure you require (as shown above in #ejwill's post)
Deserialize into this structure
List / Dictionary Accidentally Imported From a Different Namespace
Make sure that none of the imported modules have a definition for List or Dictionary
If they do you can either:
Shorten your include so that only the object(s) you need from that module (IE: using System.Text; instead of using System;)
Or add the namespace to the front of your List / Dictionary declaration (IE: System.Collections.Generic.List<string> instead of just List<string>)

C#'s version of JS's JSON [duplicate]

This question already has answers here:
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Closed 3 years ago.
I recently got into C# using Unity. I previously worked with JavaScript and know that they defiantly have their differences, but still have some similarities. I have used JSON with JS and it works great. Now with Unity, I want to store data of upcoming "stages" in JSON in an infinite runner game. But from my experience, JSON does not work nearly as well with C# as it does with JS. I have looked at Unity's JSON Utility but haven't figured out if it's possible to simply have a string and then convert it into an object which you could access like object.item.array[0].item which is how you'd do it in JS. Another thing that I looked at was this but as a novice to C#, I couldn't make heads or tails of it. So does C# have something like JSON, but its more integrated? I've used C# lists, can you get 3D lists with items and not just arrays? I know that they are very different languages, and what works well on one, might not on another.
I think closest to what you describe in your questions and the comments as
simply convert a JSON string into a JSONObject
would maybe be the good old SimpleJSON. Simply copy the SimpleJSON.cs and depending on your needs maybe SimpleJSONUnity.cs(provides some extensions for directly parsing to and from Vector2, Vector3, Vector4, Quaternion, Rect, RectOffset and Matrix4x4) somewhere into your Assets folder.
Then given the example json
{
"version": "1.0",
"data": {
"sampleArray": [
"string value",
5,
{
"name": "sub object"
}
]
}
}
you can simply access single fields like
using SimpleJSON;
...
var jsonObject = JSON.Parse(the_JSON_string);
string versionString = jsonObject["version"].Value; // versionString will be a string containing "1.0"
float versionNumber = jsonObject["version"].AsFloat; // versionNumber will be a float containing 1.0
string val = jsonObject["data"]["sampleArray"][0]; // val contains "string value"
string name = jsonObject["data"]["sampleArray"][2]["name"]; // name will be a string containing "sub object"
...
Using this you don't have to re-create the entire c# class representation of the JSON data which might sometimes be a huge overhead if you just want to access a single value from the JSON string.
However if you want to serialze and deserialize entire data structures you won't get happy using SimpleJSON. Given the example above this is how you would use Unity's JsonUtility
Create the c# class representation of the data yu want to store. In this case e.g. something like
[Serializable]
public class RootObject
{
public string version = "";
public Data data = new Data();
}
[Serializable]
public class Data
{
public List<object> sampleArray = new List<object>();
}
[Serializeable]
public class SubData
{
public string name = "";
}
Then fill it with values and parse it to JSON like
var jsonObject = new RootObject()
{
version = "1.0",
data = new Data()
{
sampleArray = new List<object>()
{
"string value",
5,
new SubData(){ name = "sub object" }
}
}
};
var jsonString = JsonUtility.ToJson(jsonObject);
And to convert it back to c# either if jsonObject was not created yet
jsonObject = JsonUtility.FromJson<RootObject>(jsonString);
otherwise
JsonUtility.FromJsonOverwrite(jsonString, jsonObject);
JSON is just how objects are represented in JavaScript. While C# can use JSON, you'll probably have a much easier time defining your own custom classes. Using classes, you can define all the properties you'll need, string them together into a list, etc.. Hope this helps.

Pass list of objects to web-service [duplicate]

This question already has answers here:
Sending array of objects to WCF
(3 answers)
Closed 5 years ago.
I have an array of objects
let arr = [{"1":"bar"},{"2":"bar"}]
which gets sent to a service through ajax inside data
the service will then get the array & do stuff.
[WebInvoke]
public void getStuff(params Model[] data)
{
// do stuff
}
what would my model need to look like to receive the data arr?
Update:
changed keys in object
You can do something like this to get the params:
var key = Request.Params[0];
then you can use the var "key" to fill a model
The elemenst in this .js array:
let arr = [{"foo":"bar"},{"foo":"bar"}]
Could be represented as this .cs class
class Model
{
public string foo;
}
Because for each object (the bit inside the {}), the default is to map the lhs to a property of the class with the same name (foo).
But, after your edit, if you want this:
let arr = [{"1":"bar"},{"2":"bar"}]
Then that cannot map to a class so easily, not leat because you can't have a field named '1' but also because it implies that there are many many different options for the lhs of the json.
In that case, consider using a Dictionary

Dynamic json object with numerical keys

I have a json object which I converted to dynamic C# object with help of this answer. It works just fine, but trouble is that this object has numerical keys. For instance,
var jsStr = "{address:{"100": {...}}}";
So I can't wirte
dynObj.address.100
And, as I know, I can't use indexers to get this object like this
dynObj.address["100"]
Please explain to me how I can get this working.
As far as I can see from the source code he resolves the properties through a private dictionary, so you have to use reflection to access the dictionary key, or modify his code a bit so that TryGetMember in DynamicJSONObject is the following (and use __numeric__ to get the key e.g. data.address.__numeric__100, and then avoid using __numeric__ as a key):
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var name = binder.Name;
//Code to check if key is of form __numeric__<number> so that numeric keys can be accessed
if (binder != null && binder.Name != null && binder.Name.StartsWith("__numeric__"))
{
name = binder.Name.Substring(11);
}
if (!_dictionary.TryGetValue(name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
{
result = new DynamicJsonObject(dictionary);
return true;
}
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
if (arrayList[0] is IDictionary<string, object>)
result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
else
result = new List<object>(arrayList.Cast<object>());
}
return true;
}
My opensource framework ImpromptuInterface has methods to call dynamic members via string name of any C# 4 dynamic object.
object tOut =Impromptu.InvokeGet(dynObj.address,"100");
I tested it with an ExpandoObject it seemed to work just fine.
An identifier must start with a
letter, underscore (_), or dollar sign
($); subsequent characters can also be
digits (0-9). Because JavaScript is
case sensitive, letters include the
characters "A" through "Z" (uppercase)
and the characters "a" through "z"
(lowercase). Starting with JavaScript
1.5, ISO 8859-1 or Unicode letters (or \uXXXX Unicode escape sequences) can
be used in identifiers.
Quoted from: http://en.wikipedia.org/wiki/JavaScript_syntax#Variables
Oh I am sorry mis understood the question, well here you go with a working example you can adjust to your needs:
<script>
var jsStr = {address:{'100': 'test'}};
var test = jsStr.address;
console.log(test);
alert(test[100]);
</script>
btw key CAN be numeric (as you see in the example in the answer), only the identifiers cannot. so you have to access just like you tried. you only have to leave away the quotes for numeric keys! and your json string will not be an object without evaluation, so in this example its strictly speaking a javascript object and not json but it doesnt matter to t he subject

Categories

Resources