Costomising the serialisation/serialised JSON in service stack - json

I need to turn this beutifull default JSON from Service Stack :
{
"Status": "ok",
"LanguageArray": [{
"Id": 1,
"Name": "English"
}, {
"Id": 2,
"Name": "Chinese"
}, {
"Id": 3,
"Name": "Portuguese"
}]
}
To this monstrosity of abomination:
{"status":"ok","language":{
"0":{"id":"1", "name":"English"},
"1":{"id":"2", "name":"Chinese"},
"2":{"id":"3", "name":"Portuguese"},
}
For reasons beyond my control or ration or logic.
My question is what are the simplest way of achieving this?
I need to do this to almost all Arrays ( they are IEnumerables implemented using List in C#)

I don't think I have a 'simplest way' and it kind of depends on where you need the serialization to happen as well as how strict the format is (case sensitive?, spacing?)
If place your example text and turn it into classes like this
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
}
public class OuterLanguage
{
public string Status { get; set; }
public IEnumerable<Language> LanguageArray { get; set; }
}
A pretty straightforward way would be having a Dto that adds a property for serialization of the Array and ignores the original Array.
public class OuterLanguageDto
{
public string Status { get; set; }
[IgnoreDataMember]
public IEnumerable<Language> LanguageArray { get; set; }
public IDictionary<string, Language> language
{
get { return this.LanguageArray.ToDictionary(x => (x.Id - 1).ToString()); }
}
}
When you need to serialize your class (OuterLanguage) you use TranslateTo (or your preferred Mapper) and copy the values over to the Dto.
var jsonObj = obj.TranslateTo<OuterLanguageDto>();
var jsonStr = new JsonSerializer<OuterLanguageDto>().SerializeToString(jsonObj);
You could also look into Custom Serialization. That would give you to have a way to serialize all your Arrays into the correct format.
JsConfig<IEnumerable<Language>>.SerializeFn = ToJson
private string ToJson(IEnumerable<Language> arr)
{
var dict = arr.ToDictionary(x => x.Id - 1);
var str = JsonSerializer.SerializeToString<Dictionary<int, Language>>(dict);
return str;
}
However, I'm guessing you may have to do some "magic string work" to take your custom serialized Array's json string and format it properly when outer class (OuterLanguage in this example) is the one being serialized.

Related

Get RAW Json with .NET Core Text.Json

I have pretty much the same question as this one:
Get Raw json string in Newtonsoft.Json Library
but now using the Text.Json.Serialization in .NET Core 3.1, I struggle to find the equivalent of JRaw.
I want to have an extensible object like:
{
"id": "myId",
"name": "name",
"description": "my description",
"extensions": {
"unit": "C°",
"minVal": "0",
"maxVal": "100",
"precision": "1",
"enum": ["str1", "str2"],
...
}
}
I want to get the Id, Name and Description set in an object, but the extensions is a bunch of properties which can be whatever. So I want to keep the RAW Json for this extensions.
public class MyData
{
public string id { get; set; }
public string name { get; set; }
public string description { get; set; }
[JsonConverter(typeof(RawJsonStringConverter))]
public string extensions { get; set; }
}
I am fighting with a custom converter as documented here:
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to
I tried with string, JsonElement, object.
No success so far.
Any idea?
Using JsonElement seems to be enough, without any custom converter:
public class MyData
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public JsonElement? Extensions { get; set; }
}
edit: JsonElement? as nullable is better: it allows JsonElement to be fully optional, if you have the corresponding settings in JsonSerializerOptions.
I am working with the following JsonSerializerOptions:
public static JsonSerializerOptions UpdateJsonSerializerOptions(this JsonSerializerOptions options, bool enumAsString = true)
{
if (options == null)
options = new JsonSerializerOptions();
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.IgnoreNullValues = true;
options.WriteIndented = true;
if (enumAsString)
{
// convert enums as strings
options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
}
return options;
}
IgnoreNullValues is the important part to have the proper conversion when no extensions at all in the input.
And CamelCase allows to stay with .NET property names (e.g 'Name') while the JSON will be with 'name'

Cannot Get JsonConvert.DeserializeObject with oData to Work

I a class that looks like so:
public class AccountAddress
{
[Key]
public int accountNumber { get; set; }
public int rowNumber { get; set; }
public string civicaddress { get; set; }
public AccountAddress()
{
//Default constructor
}
}
There is a rest API that returns a List of AccountAddress as oData that looks like this to a variable "result":
{
"#odata.context":"http://localhost:52139/odata/$metadata#WEB_V_CIVIC_ADDRESS/Values.Classes.Entities.AccountAddress","value":[
{
"#odata.type":"#Values.Classes.Entities.AccountAddress","accountNumber":123456,"rowNumber":0,"civicaddress":"123 FAKE EAST DRIVE"
},{
"#odata.type":"#Values.Classes.Entities.AccountAddress","accountNumber":123457,"rowNumber":0,"civicaddress":"123 FAKE WEST DRIVE"
}
]
}
When I try to use:
var addressAccountLookup = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AccountAddress>>(result);
I get an error
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ATPublicTAX.Regina.ca.Values.Classes.Entities.AccountAddress]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
Any help on this would be greatly appreciated.
Thanks.
You're passing the entire object to your deserialization method. You need to pass only the array, which is what it's asking you to do.
JArray array = (JArray) result["value"];
var addressAccountLookup = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AccountAddress>>(array);
Something like that should work.
The solution that I got to work is create a class:
private class oDataResponse<T>
{
public List<T> Value { get; set; }
}
Then deserialize like this:
var oDataRespone = Newtonsoft.Json.JsonConvert.DeserializeObject<oDataResponse<AccountAddress>>(result);

Fetch JSON array using WebClient and Json.Net and display it in textview?

My Activity code is:
private void MClient_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
string json = Encoding.UTF8.GetString(e.Result);
var Details = JsonConvert.DeserializeObject(json);
}
My doctor.cs class code is:
namespace App12
{
public class Doctor
{
public string DoctorId { get; set; }
public string doctorName { get; set; }
public string specialityId { get; set; }
public string experiance { get; set; }
public string fee { get; set; }
}
public class RootObject
{
public Doctor doctor { get; set; }
}
}
JSON array is:
{
"0": {
"DoctorId": "1",
"doctorName": "DR.Rama",
"specialityId": "1",
"experiance": "5 years",
"fee": "300rs"
}
}
I am trying to show this in textview but it gives me an error:
System.InvalidCastException: Specified cast is not valid.
You have two problems here.
Your class structure doesn't quite match up with the shape of the JSON data. This JSON isn't technically an array either; it's a dictionary. You need to deserialize into a Dictionary<string, Doctor>.
When calling JsonConvert.DeserializeObject you need to specify a type parameter. If you don't, the return type will be JObject. This may be the cause of the InvalidCastException.
Try it like this:
var dict = JsonConvert.DeserializeObject<Dictionary<string, Doctor>>(json);
From there you can loop through the doctors like this:
foreach (var doctor in dict.Values)
{
textView.Append(doctor.doctorName);
}

Uploading to documentDB is missing inner JSON members of complex objects

I am uploading data to the documentDB using the following code:
private static async Task UploadEntry(OptimizationData entry)
{
try
{
string endpointUrl = ConfigurationManager.AppSettings.Get("DocumentDBEndpointURL");
string authorizationKey = ConfigurationManager.AppSettings.Get("DocumentDBAuthKey");
string databaseId = ConfigurationManager.AppSettings.Get("DocumentDBDatabaseId");
string collectionName = ConfigurationManager.AppSettings.Get("CollectionName");
using (DocumentClient client = new DocumentClient(new Uri(endpointUrl), authorizationKey))
{
DocumentCollection documentCollection = client.CreateDocumentCollectionQuery("dbs/" + databaseId).Where(c => c.Id == collectionName).AsEnumerable().FirstOrDefault();
if (documentCollection != null)
{
await client.CreateDocumentAsync("dbs/" + databaseId + "/colls/" + documentCollection.Id, entry);
}
}
}
catch
{
//Ignored
}
}
Now my objects are of these types:
public class OptimizationData
{
[DataMember]
public string Id { get; set; }
[DataMember]
public Dictionary<DateTime,OptimizationEntry> Optimization;
}
And Optimization Entry looks like this:
[DataContract]
public class OptimizationEntry
{
[DataMember]
public decimal price{ get; set; }
public decimal optimalPrice { get; set; }
public decimal weight { get; set; }
}
The problem is, when I look into my document explorer on azure's portal, the inner items of the dictionary are missing. I.e., it has datetime, and the first item (price) but not optimialPrice and Weight
"Optimization": {
"2016-01-27T21:00:00": {
"price": 179.482711791992
},
"2016-01-27T22:00:00": {
"price": 196.533660888672
},
....
Is there a way I'm supposed to insert the document so that the inner objects are not missed?
To answer the question for possible future people who need help, the reason they were not showing was that the OptimizationEntry only had "price" as [DataMember] so that it could be serialized, the other two did not. Therefore JSON serialization did not include them in the upload.

Nested list with JavaScriptSerializer

I'm trying to produce the following JSON structure with JavaScriptSerializer in Net 3.5:
{
"chart": "pie",
"series": [
{
"name": "Browsers",
"data": [
[
"Firefox",
6
],
[
"MSIE",
4
]
],
"size": "43%"
}
]
}
The issue I face is regarding the data section. I cannot seem to create a class that serializes into the above structure in data.
I have read other posts which kind of touch the subject but the solution always seem to be to use JSON.Net. For instance Serializing dictionaries with JavaScriptSerializer. Although, I primaily want to solve it by using the JavaScriptSerializer or other built-in features in the .Net 3.5 framework.
How do I produce the data section?
*EDIT The way to solve it with the JavaScriptSerializer is to create an object array for the data property on the object about to be serialized.
Input parameter data is "Firefox:6\nMSIE:4\nChrome:7".
public object[] GetData(string data)
{
var dataStrArr = data.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var dataArr = new object[dataStrArr.Length];
for (var index = 0; index < dataStrArr.Length; index++)
{
var component = dataStrArr[index];
var componentArr = component.Split(':');
if (componentArr.Length != 2)
continue;
var key = componentArr[0];
decimal value;
if (decimal.TryParse(componentArr[1], out value))
{
var keyValueArr = new object[2];
keyValueArr[0] = key;
keyValueArr[1] = value;
dataArr[index] = keyValueArr;
}
}
return dataArr;
}
I think you need something like the following class (RootObject) to serialize that:
public class Series
{
public string name { get; set; }
public List<List<object>> data { get; set; }
public string size { get; set; }
}
public class RootObject
{
public string chart { get; set; }
public List<Series> series { get; set; }
}
Let me know if that helps.