I'm having an issue deserializing a JSON string into a RootObject class with 1 string property and a list of custom objects.
When I debug the application and the code deserializes the json I get my 'ErrorCode' property, "test", is populated in the RootObject class but the 'meets' property is always null.
The code sits in a cross platform Xamarin forms application currently, but I've pulled the code and class definitions out and ran them in a simple console application that worked first time with no issues. I'm struggling to figure out what I'm doing wrong.
My simple JSON object is the following:
{"meets": [
{
"VenueName": "O2"
},
{
"VenueName": "wembly"
},
{
"VenueName": "NEC"
}
],
"ErrorCode": "test"}
My class definitions:
[JsonObject(Id = "Meets")]
public class Meets
{
[JsonProperty(PropertyName = "VenueName")]
public string VenueName { get; set; }
}
public class RootObject
{
public List<Meets> Meets { get; set; }
public string ErrorCode { get; set; }
}
The code to hit the api and get the json object (which is blanked out and a smaller simple object in its place):
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;
using EliteNfcBet.Models;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace EliteNfcBet.ViewModels
{
public class ItemsViewModel : BaseViewModel
{
public ObservableCollection<Meets> RaceMeets { get; set; }
public Command LoadItemsCommand { get; set; }
public ItemsViewModel ()
{
Title = "Select Meeting";
RaceMeets = new ObservableCollection<Meets>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
}
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
try
{
RaceMeets.Clear();
var result = await GetMeetingsAsync();
//RaceMeets = result;
//foreach (var meet in meets.Meets)
//{
// RaceMeets.Add(meet);
//}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
public static async Task<RootObject> GetMeetingsAsync()
{
RootObject meet = new RootObject();
//List<Meet> meets = new List<Meet>();
HttpClient client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
var uri = new Uri(string.Format(Constants.RestUrl, string.Empty) + "/api/GetAvailablemeetings");
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
//var content = await response.Content.ReadAsStringAsync();
var content = "{\"meets\":[{\"VenueName\":\"O2\"},{\"VenueName\":\"wembly\"},{\"VenueName\":\"NEC\"}],\"ErrorCode\":\"test\"}";
if (!string.IsNullOrEmpty(content))
{
ITraceWriter traceWriter = new MemoryTraceWriter();
var settings = new JsonSerializerSettings
{
Error = (sender, args) =>
{
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
},
TraceWriter = traceWriter
};
//result = JsonConvert.DeserializeObject<T>(json, settings);
meet = JsonConvert.DeserializeObject<RootObject>(content, settings);
Console.WriteLine(traceWriter);
}
//if (meets.Count > 0)
//{
// meet.MeetList = meets;
//}
}
}
catch (Exception ex)
{
meet.ErrorCode = "INTERNAL_ERROR";
}
return meet;
}
}
}
EDIT:
I've made some minor changes suggested which are below.
My Json string is now this:
"{\"Meets\":[{\"VenueName\":\"O2\"},{\"VenueName\":\"wembly\"},{\"VenueName\":\"NEC\"}],\"ErrorCode\":\"test\"}"
My classes are below. One thing to note is they are defined within a 'models' namespace in a seperate code file that is doing the deserializing.
namespace EliteNfcBet.Models
{
public class Meets
{
public string VenueName { get; set; }
}
public class RootObject
{
public List<Meets> Meets { get; set; }
public string ErrorCode { get; set; }
}
}
Again i have debugging output when deserializing which looks like it points to the fact that the Meets class member not being found?
{2018-05-28T23:12:22.987 Info Started deserializing EliteNfcBet.Models.RootObject. Path 'Meets', line 1, position 9.
2018-05-28T23:12:22.993 Info Started deserializing System.Collections.Generic.List`1[[NInterpret.InterpretedObject, NInterpret.Xamarin.Droid, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. Path 'Meets', line 1, position 10.
2018-05-28T23:12:22.994 Info Started deserializing EliteNfcBet.Models.RootObject. Path 'Meets[0].VenueName', line 1, position 23.
2018-05-28T23:12:22.994 Verbose Could not find member 'VenueName' on EliteNfcBet.Models.RootObject. Path 'Meets[0].VenueName', line 1, position 23.
2018-05-28T23:12:22.994 Info Finished deserializing EliteNfcBet.Models.RootObject. Path 'Meets[0]', line 1, position 28.
2018-05-28T23:12:22.995 Info Started deserializing EliteNfcBet.Models.RootObject. Path 'Meets[1].VenueName', line 1, position 42.
2018-05-28T23:12:22.995 Verbose Could not find member 'VenueName' on EliteNfcBet.Models.RootObject. Path 'Meets[1].VenueName', line 1, position 42.
2018-05-28T23:12:22.996 Info Finished deserializing EliteNfcBet.Models.RootObject. Path 'Meets[1]', line 1, position 51.
2018-05-28T23:12:22.997 Info Started deserializing EliteNfcBet.Models.RootObject. Path 'Meets[2].VenueName', line 1, position 65.
2018-05-28T23:12:22.997 Verbose Could not find member 'VenueName' on EliteNfcBet.Models.RootObject. Path 'Meets[2].VenueName', line 1, position 65.
2018-05-28T23:12:22.997 Info Finished deserializing EliteNfcBet.Models.RootObject. Path 'Meets[2]', line 1, position 71.
2018-05-28T23:12:22.998 Info Finished deserializing System.Collections.Generic.List`1[[NInterpret.InterpretedObject, NInterpret.Xamarin.Droid, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. Path 'Meets', line 1, position 72.
2018-05-28T23:12:23.009 Info Finished deserializing EliteNfcBet.Models.RootObject. Path '', line 1, position 92.
2018-05-28T23:12:23.009 Verbose Deserialized JSON:
{
"Meets": [
{
"VenueName": "O2"
},
{
"VenueName": "wembly"
},
{
"VenueName": "NEC"
}
],
"ErrorCode": "test"
}}
edit 3:
I've just came across this post which explains a lot! Seems like xamarin.forms has a known problem currently with reflection and so certain packages may not work correctly.
JsonConvert.SerializeObject always return {} in XamarinForms
Does anyone have any insight as to why this happens and when this will be resolved. Also a work around that I could implement so I can use debugging. Thanks.
{"meets": [
{
"VenueName": "O2"
},
{
"VenueName": "wembly"
},
{
"VenueName": "NEC"
} ],
"ErrorCode": "test"}
Your problem is that you have an object called "meets" in javascript, and in your object in C# you have "Meets". Change it to "Meets" and it should work.
You also don't need the JsonObject attribute, I believe. .NET should be able to handle the serialization from JSON automatically:
public class Meets
{
public string VenueName { get; set; }
}
public class RootObject
{
public List<Meets> Meets { get; set; }
public string ErrorCode { get; set; }
}
Spend some time looking at these resources to understand why your error is occurring:
https://learn.microsoft.com/en-us/dotnet/standard/serialization/
EDIT
The above classes produces the following json:
Meets:
{
"VenueName": null
}
Root Object:
{
"Meets": [
{
"VenueName": null
},
{
"VenueName": null
},
{
"VenueName": null
}
],
"ErrorCode": null
}
Related
This question already has answers here:
My console app shutdown prematurely when using async / await?
(4 answers)
Program exits upon calling await
(3 answers)
Closed 2 years ago.
Before you all go on a rampage about how this is a duplicate question, I have spent two days working on this issue, watching youtube tutorials on asynchronous programming, surfing similar stackoverflow posts etc, and I cannot for the life of me figure out how to apply Asynchronous Parallel Downloading of files into my project.
First things first, some background:
I am creating a program that, when given a query input via the user, will make a call to the twitch API and download clips.
My program is two parts
1- A web scraper that generates a .json file with all details needed to download files and
2 - A downloader.
Part 1 works perfectly fine and generates the .json files no trouble.
My Downloader contains reference to a Data class that is a handler for common properties and methods like my ClientID, Authentication, OutputPath, JsonFile, QueryURL. It also contains methods to give values to these properties.
Here are the two methods of my FileDownloader.cs that are the problem:
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach(ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
This is only one of my many attempts of downloading files, one which I got the idea from this post:
stackoverflow_link
I have also tried methods like the following from a YouTube video by IAmTimCorey:
video_link
I have spent many an hour tackling this problem, and I honestly can't figure out why it won't work with any of my attempts. I would vastly appreciate your help.
Thanks,
Ben
Below is the entirety of my code, should anyone need it for any reason.
Code Structure:
The only external libraries I have downloaded is Newtonsoft.Json
ClipInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Downloader
{
public class ClipInfo
{
public string id { get; set; }
public string url { get; set; }
public string embed_url { get; set; }
public string broadcaster_id { get; set; }
public string broadcaster_name { get; set; }
public string creator_id { get; set; }
public string creator_name { get; set; }
public string video_id { get; set; }
public string game_id { get; set; }
public string language { get; set; }
public string title { get; set; }
public int view_count { get; set; }
public DateTime created_at { get; set; }
public string thumbnail_url { get; set; }
}
}
Pagination.cs
namespace Downloader
{
public class Pagination
{
public string cursor { get; set; }
}
}
Root.cs
using System.Collections.Generic;
namespace Downloader
{
public class Root
{
public List<ClipInfo> data { get; set; }
public Pagination pagination { get; set; }
}
}
Data.cs
using System;
using System.IO;
namespace Downloader
{
public class Data
{
private static string directory = Directory.GetCurrentDirectory();
private readonly static string defaultJsonFile = directory + #"\clips.json";
private readonly static string defaultOutputPath = directory + #"\Clips\";
private readonly static string clipsLink = "https://api.twitch.tv/helix/clips?";
public string OutputPath { get; set; }
public string JsonFile { get; set; }
public string ClientID { get; private set; }
public string Authentication { get; private set; }
public string QueryURL { get; private set; }
public Data()
{
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication, string outputPath)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = directory + #"\" + outputPath + #"\";
JsonFile = OutputPath + outputPath + ".json";
}
public void GetQuery()
{
Console.Write("Please enter your query: ");
QueryURL = clipsLink + Console.ReadLine();
}
public void GetClientID()
{
Console.WriteLine("Enter your client ID");
ClientID = Console.ReadLine();
}
public void GetAuthentication()
{
Console.WriteLine("Enter your Authentication");
Authentication = Console.ReadLine();
}
public void OutputFolderExists()
{
if (!Directory.Exists(OutputPath))
{
Directory.CreateDirectory(OutputPath);
}
}
}
}
JsonGenerator.cs
using System;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Linq;
namespace Downloader
{
public static class JsonGenerator
{
// This class has no constructor.
// You call the Generate methods, passing in all required data.
// The file will then be generated.
private static Data data;
public static async Task Generate(Data clientData)
{
data = clientData;
string responseContent = null;
// Loop that runs until the api request goes through
bool authError = true;
while (authError)
{
authError = false;
try
{
responseContent = await GetHttpResponse();
}
catch (HttpRequestException)
{
Console.WriteLine("Invalid authentication, please enter client-ID and authentication again!");
data.GetClientID();
data.GetAuthentication();
authError = true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
authError = true;
}
}
data.OutputFolderExists();
GenerateJson(responseContent);
}
// Returns the contents of the resopnse to the api call as a string
private static async Task<string> GetHttpResponse()
{
// Creating client
HttpClient client = new HttpClient();
if (data.QueryURL == null)
{
data.GetQuery();
}
// Setting up request
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, data.QueryURL);
// Adding Headers to request
requestMessage.Headers.Add("client-id", data.ClientID);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", data.Authentication);
// Receiving response to the request
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage);
// Gets the content of the response as a string
string responseContent = await responseMessage.Content.ReadAsStringAsync();
return responseContent;
}
// Generates or adds to the .json file that contains data on each clip
private static void GenerateJson(string responseContent)
{
// Parses the data from the response to the api request
Root responseResult = JsonConvert.DeserializeObject<Root>(responseContent);
// If the file doesn't exist, we need to create it and add a '[' at the start
if (!File.Exists(data.JsonFile))
{
FileStream file = File.Create(data.JsonFile);
file.Close();
// The array of json objects needs to be wrapped inside []
File.AppendAllText(data.JsonFile, "[\n");
}
else
{
// For a pre-existing .json file, The last object won't have a comma at the
// end of it so we need to add it now, before we add more objects
string[] jsonLines = File.ReadAllLines(data.JsonFile);
File.WriteAllLines(data.JsonFile, jsonLines.Take(jsonLines.Length - 1).ToArray());
File.AppendAllText(data.JsonFile, ",");
}
// If the file already exists, but there was no [ at the start for whatever reason,
// we need to add it
if (File.ReadAllText(data.JsonFile).Length == 0 || File.ReadAllText(data.JsonFile)[0] != '[')
{
File.WriteAllText(data.JsonFile, "[\n" + File.ReadAllText(data.JsonFile));
}
string json;
// Loops through each ClipInfo object that the api returned
for (int i = 0; i < responseResult.data.Count; i++)
{
// Serializes the ClipInfo object into a json style string
json = JsonConvert.SerializeObject(responseResult.data[i]);
// Adds the serialized contents of ClipInfo to the .json file
File.AppendAllText(data.JsonFile, json);
if (i != responseResult.data.Count - 1)
{
// All objects except the last require a comma at the end of the
// object in order to correctly format the array of json objects
File.AppendAllText(data.JsonFile, ",");
}
// Adds new line after object entry
File.AppendAllText(data.JsonFile, "\n");
}
// Adds the ] at the end of the file to close off the json objects array
File.AppendAllText(data.JsonFile, "]");
}
}
}
FileDownloader.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace Downloader
{
public class FileDownloader
{
private static Data data;
private static List<Task> tasks;
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach (ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private static void GetData()
{
if (data.ClientID == null)
{
data.GetClientID();
}
if (data.Authentication == null)
{
data.GetAuthentication();
}
if (data.QueryURL == null)
{
data.GetQuery();
}
}
private static string GetClipURL(ClipInfo clip)
{
// Example thumbnail URL:
// https://clips-media-assets2.twitch.tv/AT-cm%7C902106752-preview-480x272.jpg
// You can get the URL of the location of clip.mp4
// by removing the -preview.... from the thumbnail url */
string url = clip.thumbnail_url;
url = url.Substring(0, url.IndexOf("-preview")) + ".mp4";
return url;
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
private static void FileDownloadComplete(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
tasks.Remove((Task)sender);
}
}
}
Program.cs
using System;
using System.Threading.Tasks;
using Downloader;
namespace ClipDownloader
{
class Program
{
private static string clientID = "{your_client_id}";
private static string authentication = "{your_authentication}";
async static Task Main(string[] args)
{
Console.WriteLine("Enter your output path");
string outputPath = Console.ReadLine();
Data data = new Data(clientID, authentication, outputPath);
Console.WriteLine(data.OutputPath);
//await JsonGenerator.Generate(data);
FileDownloader.DownloadAllFiles(data);
}
}
}
The example query I usually type in is "game_id=510218"
async void is your problem
Change
public static async void DownloadAllFiles(Data clientData)
To
public static async Task DownloadAllFiles(Data clientData)
Then you can await it
await FileDownloader.DownloadAllFiles(data);
The longer story:
async void runs unobserved (fire and forget). You can't wait for them to finish. In essence as soon as your program starts the task, it finishes, and tears down the App Domain and all your sub tasks, leading you to believe nothing is working.
I'm trying to stay on topic here as best as I can, but when using JsonConvert.DeserializeObject{T}, isn't T suppose to be an encapsulating root object type? I have never used it the way you're using it, so I'm just curious if that might be your bug. I could be completely wrong, and spare me if i am, but JSON is key:value based. Deserializing directly to a List doesn't really make sense. Unless there is a special case in the deserializer? List would be a file that's purely an array of ClipInfo values being deserialized into the members of List{T}(private T[] _items, private int _size, etc.) It needs a parent root object.
// current JSON file format implication(which i dont think is valid JSON?(correct me please)
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
// correct(?) JSON file format
{ // { } is the outer encasing object
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
}
class ClipInfoJSONFile
{
public List<ClipInfo> Info { get; set; }
}
var clipInfoList = JsonConverter.DeserializeObject<ClipInfoJSONFile>(...);
I have a web api controller in .NET Core 2.1, which receives
JToken jsonBody
The json has the following structure
{
"id": "xxx",
"payload": {
"TelephoneNumber": "1111",
"Name": "Hans"
}
}
and more fields, but it's irrelevant.
I want to retrieve the Number and Name elegantly. Currently, I do the following, which I'm sure could be done in a nicer way:
var payload = JObject.Parse(jsonBody.SelectToken("Payload").ToString());
telephoneNumber = new TelephoneNumber(payload.SelectToken("TelephoneNumber").ToString());
I've tried just doing
jsonBody.SelectToken("Payload.TelephoneNumber")
but that doesn't work. I think that it's because somehow the jsonBody, that the controller receives, has only parsed the top nodes as json, hence it could be that it regards the value of
jsonBody.SelectToken("Payload")
as a string.
As per official documentation - you can do something like this:
var phone = jsonBody["payload"]["TelephoneNumber"].ToString();
var name = jsonBody["payload"]["Name"].ToString();
See a live demo on rextester.
This is at least a little bit more elegant:
var jsonBody = JObject.Parse(#"{
'id': 'xxx',
'payload': {
'TelephoneNumber': '1111',
'Name': 'Hans'
}
}");
var phone = jsonBody["payload"]["TelephoneNumber"].Value<string>();
var name = jsonBody["payload"]["Name"].Value<string>();
If you don't want to deserialize your full json, you can create a class with the properties you need
public class Payload
{
public string TelephoneNumber { get; set; }
public string Name { get; set; }
}
And then use JsonTextReader to deserialize the string:
private static Payload DeserializePayload(JToken token)
{
var serializer = new JsonSerializer();
using (JsonTextReader reader = new JsonTextReader(new StringReader(token.ToString())))
{
reader.CloseInput = true;
while (reader.Read())
{
if (reader.TokenType == JsonToken.StartObject && reader.Path.Equals("payload"))
{
var payload = serializer.Deserialize<Payload>(reader);
return payload;
}
}
}
// not found - return null? throw exception?
return null;
}
Testing the code:
var token = JToken.Parse(#"{
""id"": ""xxx"",
""payload"": {
""TelephoneNumber"": ""1111"",
""Name"": ""Hans""
}
}");
Payload payload = DeserializePayload(token);
Console.WriteLine($"Name: {payload.Name}, Phone number: {payload.TelephoneNumber}");
I am working on 2 web applications; A & B. now i have a shared class named CRUDOutput as follow on both web applications:-
public class CRUDOutput
{
public Operation4 operation { get; set; }
}
public class Operation4
{
public Result result { get; set; }
public string name { get; set; }
}
public class Result
{
public string status { get; set; }
public string message { get; set; }
}
now inside web application A i am returning the following:-
[HttpPost]
public ActionResult CreateResource(CreateResource cr)
{
List<CRUDOutput> co = new List<CRUDOutput>();
co.Add(JsonConvert.DeserializeObject<CRUDOutput>(crudoutput));
co.Add(JsonConvert.DeserializeObject<CRUDOutput>(crudoutput2));
return Json(JsonConvert.SerializeObject(co));
}
now from web application B, i am calling the action method as follow:-
try
{
using (WebClient wc = new WebClient())
{
string url = "https://localhost:44302/" + "Home/CreateResource";
Uri uri = new Uri(url);
wc.Headers.Add(HttpRequestHeader.ContentType, "application/json");
output = wc.UploadString(uri, data);
}
}
catch (WebException e)
{
}
List<CRUDOutput> result = JsonConvert.DeserializeObject<List< CRUDOutput>>(output);
but i will get the following exception when i tried to deserialize the output:-
Error converting value
"[{"operation":{"result":{"status":"Success","message":"Resource has
been added successfully to ......"},"name":"CREATE
RESOURCE"}},{"operation":{"result":{"status":"Failed","message":"Account
addition "},"name":"ADD ACCOUNTS"}}]" to type
'System.Collections.Generic.List`1[S.ViewModels.CRUDOutput]'. Path '',
line 1, position 464.
now the JSON return from web application A will be as follow:-
"\"[{\\\"operation\\\":{\\\"result\\\":{\\\"status\\\":\\\"Success\\\",\\\"message\\\":\\\"Resource 123 rfrf has been added successfully \\\"},\\\"name\\\":\\\"CREATE RESOURCE\\\"}},{\\\"operation\\\":{\\\"result\\\":{\\\"status\\\":\\\"Failed\\\",\\\"message\\\":\\\"Account addition \\\"},\\\"name\\\":\\\"ADD ACCOUNTS\\\"}}]\""
so can anyone advice why i am unable to deserialize to a list of objects?
The output as you've pasted is encoded as JSON twice. Compare the difference between:
"\"[{\\\"operation\\\":{\\\"result\\\":{\\\"status\\\":\\\"Success\\\",\\\"message\\\":\\\"Resource 123 rfrf has been added successfully \\\"},\\\"name\\\":\\\"CREATE RESOURCE\\\"}},{\\\"operation\\\":{\\\"result\\\":{\\\"status\\\":\\\"Failed\\\",\\\"message\\\":\\\"Account addition \\\"},\\\"name\\\":\\\"ADD ACCOUNTS\\\"}}]\""
and
"[{\"operation\":{\"result\":{\"status\":\"Success\",\"message\":\"Resource 123 rfrf has been added successfully \"},\"name\":\"CREATE RESOURCE\"}},{\"operation\":{\"result\":{\"status\":\"Failed\",\"message\":\"Account addition \"},\"name\":\"ADD ACCOUNTS\"}}]"
This happens because you're encoding the result as Json twice. Replace:
return Json(JsonConvert.SerializeObject(result));
with
return Json(result); // This encodes as JSON automatically
I am trying to pass a json string to a C#-Program using Commandline.
The JSON-String looks like this:
{
"config": {
"script": {
"script_name": "test",
"dir": "D:\\test",
"destination": "M:\\neu\\test",
"params": "/b /s /r:3 /w:5"
}
}
}
In Commandline it looks like this:
{"config":{"script":{"script_name":"test","dir":"D:\\test","destination":"M:\\neu\\test","params":"/b /s /r:3 /w:5"}}}
But if I just pass the string then it gets chunked into several pieces. But I want my program to see it as just a single string.
Do I have to adapt my JSON-String?
Declare it as a string with "" and escape the other " with \ and it should work.
Command line:
"{\"config\":{\"script\":{\"script_name\":\"test\",\"dir\":\"D:\\test\",\"destination\":\"M:\\neu\\test\",\"params\":\"/b /s /r:3 /w:5\"}}}"
This should work:
var jsonString = Environment.CommandLine;
I tested it with the debugger like so:
var jsonString = Environment.CommandLine;
// (*) This correction makes it work, although it is pretty ugly:
jsonString = jsonString.Split(new string[] { ".exe\" " }, StringSplitOptions.None)[1];
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(jsonString);
Debugging with VS2015, and not modifying the json input (not even removing the line changes). I am using the same structure as your input:
public class Script
{
public string script_name { get; set; }
public string dir { get; set; }
public string destination { get; set; }
public string #params { get; set; }
}
public class Config
{
public Script script { get; set; }
}
public class RootObject
{
public Config config { get; set; }
}
About (*) => The problem with the deserialization is that the exe info is added in front of the command line with Environment.CommandLine and it "pollutes" the json like this: jsonString =
"path\to\assembly\name.vshost.exe" {
"config": {
"script": {
"script_name": "test",
"dir": "D:\\test",
"destination": "M:\\neu\\test",
"params": "/b /s /r:3 /w:5"
}
}
}
If anybody has a prettier fix to this problem please let me know.
Try to save the JSON object into a file, and pass the file as the argument to your application.
#Wildcard27 :
This is an actual use case in order to create Windows Tasks which was used for the faculty degree app. The JSON was just a simple serialization of a DTO that I was using.
When you serialize the JSON, just save it into a blank file, giving it a proper name so that is unique.
private string CreateTaskConfigurationFile(string taskName, EquipmentEventExtended eventData, string host)
{
List<Change> changes = new List<Change>
{
new Change(MailConstants.EventName,eventData.EventName),
new Change(MailConstants.Deadline, eventData.DateTo.Value.ToShortDateString()),
new Change(MailConstants.EventDetails, eventData.EventDetails),
new Change(MailConstants.Link,$"{host}/Inventory/Details/{eventData.InventoryId}")
};
MailTaskModel mtm = new MailTaskModel
{
Body = MailConstants.UpdateTemplate(MailConstants.TaskMailTemplate, changes),
Subject = "[Reminder] Upcoming Event needs your attention",
ToAddress = "abcdef#gmail.com",
IsHtml = true
};
var fileName = string.Format(#"E:\{0}.json", taskName);
using (StreamWriter file = File.CreateText(fileName))
{
JsonSerializer js = new JsonSerializer();
js.Serialize(file, mtm);
}
return fileName;
}
Then you provide the file path as an argument to the console application:
static void Main(string[] args)
{
var configFilePath = args[0];
var mailConfig = LoadConfigurationFile(configFilePath);
MailManager manager = new MailManager(mailConfig.ToAddress, mailConfig.FromAddress,mailConfig.Subject, mailConfig.Body,mailConfig.IsHtml);
manager.SendMail();
}
private static MailTaskModel LoadConfigurationFile(string configurationFilePath)
{
MailTaskModel mailConfig;
using(var sr = new StreamReader(configurationFilePath))
{
string json = sr.ReadToEnd();
mailConfig = JsonConvert.DeserializeObject<MailTaskModel>(json);
}
return mailConfig;
}
You can then use something like
ConsoleApplication.exe -yourFilePath
I've removed noisy check-ups for nulls and all that so that it's more clear.
Instead of looking at the "string[] args" you could use Environment.CommandLine.
From MSDN https://msdn.microsoft.com/en-us/library/system.environment.commandline.aspx
public static void Main()
{
Console.WriteLine();
// Invoke this sample with an arbitrary set of command line arguments.
Console.WriteLine("CommandLine: {0}", Environment.CommandLine);
}
// The example displays output like the following:
// C:>env0 ARBITRARY TEXT
//
// CommandLine: env0 ARBITRARY TEXT
Just send json value to commandline after catch value and replace it. It's work for me.
args[1].Replace("{","{\"").Replace(":","\":\"").Replace(",","\",\"").Replace("}","\"}");
Following on from #Selcuk Gurals post, here is a more complete answer:
args[1].Replace("{", "{\"").Replace(":", "\":\"").Replace(",", "\",\"").Replace("}", "\"}").Replace(":\"[", ":[").Replace(":\"{", ":{").Replace("https\":\"", "https:").Replace("http\":\"", "http:").Replace("\":\"9", ":9").Replace("}\",", "},").Replace("]\",", "],").Replace("}\"}", "}}");
This caters for things like embedded http/https and ports. My port number was in the 9000 region... So a regex solution would be better. But it improves on the former answer
The value part of a JSON key/value pair can also be:
another JSON object
a list
"key": {}, ....
"key":[], ....
Given below is the type of JSON response ,
{
"?xml":{
"#version":"1.0",
"#encoding":"iso-8859-1"
},
"xmlreport":{
"#title":"ABC: TEST Most Saved2",
"#dates":"Week of May 19,2013",
"columns":{
"column":[
{
"#name":"Page",
"#type":"dimension",
"#text":"Page"
},
{
"#name":"Events",
"#type":"metric",
"#hastotals":"true",
"#text":"Events"
}
]
},
"rows":{
"row":[
{
"#rownum":"1",
"cell":[
{
"#columnname":"page",
"#csv":"\"http://www.ABC.com/profile/recipebox\"",
"#text":"http://www.ABC.com/profile/recipebox"
},
{
"#columnname":"events",
"#percentage":"\"0.1%\"",
"#text":"489"
}
]
},
{
"#rownum":"2",
"cell":[
{
"#columnname":"page",
"#csv":"\"http://www.ABC.com/recipes/peanut-butter-truffle-brownies/c5c602e4-007b-43e0-aaab-2f9aed89524c\"",
"#text":"http://www.ABC.com/recip...c602e4-007b-43e0-aaab-2f9aed89524c"
},
{
"#columnname":"events",
"#percentage":"\"0.0%\"",
"#text":"380"
}
]
}
]
},
"totals":{
"pagetotals":{
"total":{
"#columnname":"events",
"#value":"1820.000000",
"#text":"1,820 (0.2%)"
}
},
"reporttotals":{
"total":{
"#columnname":"events",
"#value":"7838.000000",
"#text":"7,838 (0.8%)"
}
},
"timeperiodtotals":{
"total":{
"#columnname":"events",
"#value":"955774.000000",
"#text":"955,774 (100.0%)"
}
}
}
}
}
I am unable to parse the object.Could you please help me out how do I read the attributes and elements after parsing. I am using C#
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(XML);
string jsonText = JsonConvert.SerializeXmlNode(doc);
//var result = Newtonsoft.Json.JsonConvert.DeserializeXmlNode(jsonText, "xmlreport");
var results = JsonConvert.DeserializeObject<dynamic>(jsonText);
JToken token = JObject.Parse(jsonText);
var report = token["xmlreport"];
}
My understanding of the question is you've got some Xml and you need to send out json. Couple of points before we get to the code:
1) Don't convert xml to json directly as it causes issues
2) Parse the xml to objects at your end and then work out the format to return; decoupling what comes in and goes out will allow for one of the interfaces to change in the future without impacting the other as you can tweak the mapping
So, onto the code ...
Essentially parse the xml to objects to allow for further processing and then push out as json.
class Program
{
private static string starting =
"<xmlreport title=\"ABC: TEST Most Saved2\" dates=\"Week of May 19,2013\"><columns><column name=\"Page\" type=\"dimension\">Page</column><column name=\"Events\" type=\"metric\" hastotals=\"true\">Events</column></columns><rows><row rownum=\"1\"><cell columnname=\"page\" csv=\"http://www.ABC.com/profile/recipebox\">http://www.ABC.com/profile/recipebox</cell><cell columnname=\"events\" percentage=\"0.1%\">489</cell></row><row rownum=\"2\"><cell columnname=\"page\" csv=\"http://www.ABC.com/recipes/peanut-butter-truffle-brownies/c5c602e4-007b-43e0-aaab-2f9aed89524c\">http://www.ABC.com/recipes/peanut-butter-truffle-brownies/c5c602e4-007b-43e0-aaab-2f9aed89524c</cell><cell columnname=\"events\" percentage=\"0.0%\">380</cell></row></rows><totals><pagetotals><total columnname=\"events\" value=\"1820.00000\">1,820 (0.2%)</total></pagetotals><reporttotals><total columnname=\"events\" value=\"7838.000000\">7,838 (0.8%)</total></reporttotals><timeperiodtotals><total columnname=\"events\" value=\"955774.000000\">955,774 (100.0%)</total></timeperiodtotals></totals></xmlreport>";
static void Main(string[] args)
{
// parse from xml to objects
StringReader reader = new StringReader(starting);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(XmlReport));
var xmlreport = (XmlReport)xmlSerializer.Deserialize(reader);
// todo: do some process mapping ...
// parse out as json
var json = JsonConvert.SerializeObject(xmlreport);
Console.WriteLine(json);
Console.ReadLine();
}
}
[Serializable]
[XmlRoot(ElementName = "xmlreport")]
public class XmlReport
{
[XmlAttribute(AttributeName = "title")]
public string Title { get; set; }
[XmlAttribute(AttributeName = "dates")]
public string Dates { get; set; }
[XmlArray(ElementName = "columns")]
[XmlArrayItem(typeof(Column), ElementName = "column")]
public Collection<Column> Columns { get; set; }
}
[Serializable]
public class Column
{
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
}
I've tried to parse the json to the original xml to begin with so appologies if I've not interpreted it properly. I've not done the entire structure but I hope the example above gives you an idea of how to do the rest.
Hope this helps.
One way of doing this is by getting the actual Data Structure of that JSON object that you have there.
Then create classes representing that data structure (if you can get a complete response -- having all properties, you just use this site to convert that to classes).
After that, deserialize that JSON object into your class using different libraries available. A sample could be this one.
Use this (JSon.NET) and a recursive function writes all keys and values to output window.
[TestMethod]
public void ParseMePlease()
{
string s = #"{""?xml"":{""#version"":""1.0"",""#encoding"":""iso-8859-1""},
""xmlreport"":{""#title"":""ABC: TEST Most Saved2"",""#dates"":""Week of May 19,2013"",
""columns"":{""column"":[{""#name"":""Page"",""#type"":""dimension"",""#text"":""Page""},{""#name"":""Events"",""#type"":""metric"",""#hastotals"":""true"",""#text"":""Events""}]},
""rows"":
{""row"":[{""#rownum"":""1"",""cell"":[{""#columnname"":""page"",""#csv"":""\""http://www.ABC.com/profile/recipebox\"""",""#text"":""http://www.ABC.com/profile/recipebox""},{""#columnname"":""events"",""#percentage"":""\""0.1%\"""",""#text"":""489""}]},
{""#rownum"":""2"",""cell"":[{""#columnname"":""page"",""#csv"":""\""http://www.ABC.com/recipes/peanut-butter-truffle-brownies/c5c602e4-007b-43e0-aaab-2f9aed89524c\"""",""#text"":""http://www.ABC.com/recip...c602e4-007b-43e0-aaab-2f9aed89524c""},{""#columnname"":""events"",""#percentage"":""\""0.0%\"""",""#text"":""380""}]}]},
""totals"":{""pagetotals"":{""total"":{""#columnname"":""events"",""#value"":""1820.000000"",""#text"":""1,820 (0.2%)""}},
""reporttotals"":{""total"":{""#columnname"":""events"",""#value"":""7838.000000"",""#text"":""7,838 (0.8%)""}},
""timeperiodtotals"":{""total"":{""#columnname"":""events"",""#value"":""955774.000000"",""#text"":""955,774 (100.0%)""}}}}}";
var result=JsonConvert.DeserializeObject<object>(s);
Debug.WriteLine("Right Click Result on quick watch for Result Views!(On Debug)"+result.ToString() );
JObject jobject = ((Newtonsoft.Json.Linq.JObject)result);
PrintDetail(jobject);
}
public void PrintDetail(JObject node)
{
foreach (var item in node)
{
Debug.WriteLine("Key:" + item.Key + " Value:" + item.Value);
if (item.Value is JObject)
{
PrintDetail((JObject)item.Value);
}
}
}