not really sure if I'm asking this in the correct manner. But I am doing a project for my university with CRM systems and API's.
Now I found Flurl to help me do my HTTP request. and it works great until I try and do a get all accounts to my free developer account to salesforce (i added some test accounts). The JSON I receive is this:
{
"objectDescribe": {
"activateable": false,
"createable": true,
"custom": false,
"customSetting": false,
"deletable": true,
"deprecatedAndHidden": false,
"feedEnabled": true,
"hasSubtypes": false,
"isSubtype": false,
"keyPrefix": "001",
"label": "Account",
"labelPlural": "Accounts",
"layoutable": true,
"mergeable": true,
"mruEnabled": true,
"name": "Account",
"queryable": true,
"replicateable": true,
"retrieveable": true,
"searchable": true,
"triggerable": true,
"undeletable": true,
"updateable": true,
"urls": {
"compactLayouts": "/services/data/v39.0/sobjects/Account/describe/compactLayouts",
"rowTemplate": "/services/data/v39.0/sobjects/Account/{ID}",
"approvalLayouts": "/services/data/v39.0/sobjects/Account/describe/approvalLayouts",
"defaultValues": "/services/data/v39.0/sobjects/Account/defaultValues?recordTypeId&fields",
"listviews": "/services/data/v39.0/sobjects/Account/listviews",
"describe": "/services/data/v39.0/sobjects/Account/describe",
"quickActions": "/services/data/v39.0/sobjects/Account/quickActions",
"layouts": "/services/data/v39.0/sobjects/Account/describe/layouts",
"sobject": "/services/data/v39.0/sobjects/Account"
}
},
"recentItems": [
{
"attributes": {
"type": "Account",
"url": "/services/data/v39.0/sobjects/Account/0015800000it9T3AAI"
},
"Id": "0015800000it9T3AAI",
"Name": "Test 5"
},
{
"attributes": {
"type": "Account",
"url": "/services/data/v39.0/sobjects/Account/0015800000it8eAAAQ"
},
"Id": "0015800000it8eAAAQ",
"Name": "Test 4"
},
{
"attributes": {
"type": "Account",
"url": "/services/data/v39.0/sobjects/Account/0015800000it8dbAAA"
},
"Id": "0015800000it8dbAAA",
"Name": "Test 3"
},
{
"attributes": {
"type": "Account",
"url": "/services/data/v39.0/sobjects/Account/0015800000it8dHAAQ"
},
"Id": "0015800000it8dHAAQ",
"Name": "Test 2"
},
{
"attributes": {
"type": "Account",
"url": "/services/data/v39.0/sobjects/Account/0015800000it8ciAAA"
},
"Id": "0015800000it8ciAAA",
"Name": "Test 1"
}
]
}
and the error I receive is the following:
Request to https://eu6.salesforce.com/services/data/v39.0/sobjects/Account/ failed.
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[InHollandCRMAPI.Models.AccountItem]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object.
JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'objectDescribe', line 1, position 18.
I also found this link on here:
Parsing from json to object using FLURL
but I can't seem to recreate this with my model:
public class AccountItem : ICRMItem
{
public Describe[] ObjectDescribe { get; set; }
public List<Recent> recentItems { get; set; }
public class Recent
{
public Attributes[] Attributes { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class Describe
{
public bool activateable { get; set; }
public bool createable { get; set; }
public bool custom { get; set; }
public bool customSetting { get; set; }
public bool deletable { get; set; }
public bool deprecatedAndHidden { get; set; }
public bool feedEnabled { get; set; }
public bool hasSubtypes { get; set; }
public bool isSubtype { get; set; }
public string keyPrefix { get; set; }
public string label { get; set; }
public string labelPlural { get; set; }
public bool layoutable { get; set; }
public bool mergeable { get; set; }
public bool mruEnabled { get; set; }
public string name { get; set; }
public bool queryable { get; set; }
public bool replicateable { get; set; }
public bool retrieveable { get; set; }
public bool searchable { get; set; }
public bool triggerable { get; set; }
public bool undeletable { get; set; }
public bool updateable { get; set; }
public Urls[] urls { get; set; }
}
}
and at last this is how de Deserialize is in my code
response = request.GetAsync();
responseData = await response.ReceiveJson<T>().ConfigureAwait(true);
Edit my controller class where the requests come in:
[HttpGet("{CRM}")]
public IEnumerable<ICRMItem> Get(string CRM)
{
if(CRM == "SalesForce")
{
ICRMService AccountGetAll = new AccountService();
var Account = AccountGetAll.With<AccountItem>().GetAll().ResponseData();
return Account;
}
}
After #Todd Menier his changes
as my response in Todd's message shamefully it didn't do the trick. and i still get this exception message.
Request to https://eu6.salesforce.com/services/data/v39.0/sobjects/Account/ ailed. Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type
'System.Collections.Generic.IEnumerable`1[InHollandCRMAPI.Models.AccountItem]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>)
that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'objectDescribe', line 1, position 18.
Edit
Todd Menier asked me for the path my code takes so here it is:
After I do my call it comes in my controller
ICRMService AccountGetAll = new AccountService();
var Account = AccountGetAll.With<AccountItem>().GetAll().ResponseData();
return Account;
Where after it goes into my service:
public ICRMServiceWithResource<T> With<T>(bool beta = false) where T : ICRMItem
{
var Uri = "https://eu6.salesforce.com/services/data/v39.0/";
return new SalesForceServiceWithResource<T>()
{
Resource = Resources.Resources.GetResource<T>(),
Uri = Uri
};
}
then it gets the Recources
public class Resources
{
public const string Accounts = "sobjects/Account/";
public static string GetResource<T>() where T : ICRMItem
{
var type = typeof(T);
if (type == typeof(AccountItem)) return Accounts;
and it gets into my GetAll function
public ICRMResponse<IEnumerable<T>> GetAll()
{
return Get<IEnumerable<T>>();
}
as you see it goes to a get function
private ICRMResponse<TOut> Get<TOut>(string id = "")
{
return DoRequest<TOut>(Resource + id, "GET", null).Result;
}
from where it goes into the DoRequest:
public async Task<ICRMResponse<T>> DoRequest<T>(string url, string method, object body)
{
ICRMResponse<T> result;
try
{
GetCRM(AppConfig.Key);
var request = Authorise(url);
Task<HttpResponseMessage> response;
T responseData;
switch (method.ToLower())
{
case "post":
if (body == null)
{
throw new ArgumentNullException("body");
}
response = request.PostJsonAsync(body);
responseData = await response.ReceiveJson<T>().ConfigureAwait(false);
break;
case "get":
response = request.GetAsync();
responseData = await response.ReceiveJson<T>().ConfigureAwait(true);
break;
from where it breaks and shows the message as state before
i'll check back around 16:00 GMT+1 or else Tuesday morning hope i gave you everything you needed
In your C# model, urls is an array, but it is not an array in the JSON representation; it is an object.
You didn't post the definition of your Urls class, but I'm going to guess it looks like this:
public class Urls
{
public string compactLayouts { get; set; }
public string rowTemplate { get; set; }
public string approvalLayouts { get; set; }
public string defaultValues { get; set; }
public string listviews { get; set; }
public string describe { get; set; }
public string quickActions { get; set; }
public string layouts { get; set; }
public string sobject { get; set; }
}
The JSON is returning a single object that looks like this, not a collection of them. So, in your Describe class, just drop the array and define it like this:
public Urls urls { get; set; }
UPDATE:
I didn't see this at first but there's a similar problem with objectDescribe and attributes. Those aren't arrays either, so don't define them that way in your C# model. Here's a summary of all changes:
public class AccountItem : ICRMItem
{
public Describe ObjectDescribe { get; set; }
...
public class Recent
{
public Attributes Attributes { get; set; }
...
}
public class Describe
{
...
public Urls urls { get; set; }
}
}
Soo, I just fixed the Error. Was trying to get a List of all Accounts. but salesforce already Lists (part) the Accounts for you.
The Issue was IEnumerable in the GetAll() function (wich works great for most CRM systems) but if you wanna get this done you'd need to change the IEnumerable<T> to just T and that would be the quick fix for SalesForce
The Next step for me is to generate a List of accounts with all account info (as most GetAll's work with CRM API's).
TL;DR
public ICRMResponse<IEnumerable<T>> GetAll()
{
return Get<IEnumerable<T>>();
}
should be
public ICRMResponse<T> GetAll() //Technicly isn't a GetAll But a Get
{
return DoRequest<T>(Resource, "GET", null).Result;
}
This is a Fix to this post not a fix to my eventual wish but I'll close this topic because else we will go off topic
Related
I am using ASP.net core web api and angular 10. When I make a post request from angular project, it is passing the correct json object. It hit the POST method on my api side. Than I try to deserializeObject but it is throwing error.
ERROR:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject<API.ViewModel.EmpDepViewMode>(string)' has some invalid arguments'
JSON I am passing
{
"Employees": [
{
"Name": "John Doe",
"yearlySalary": "50000",
"numberOfPayCheck": 26
}
],
"Dependents": [
{
"Type": 0,
"Name": "Maria Smith"
}
]
}
EmpDepViewModel:
public class EmpDepViewMode
{
public List<Employee> Employees { get; set; } = new List<Employee>();
public List<Dependent> Dependents { get; set; } = new List<Dependent>();
}
Employee class:
public class Employee
{
public string Name { get; set; }
public int yearlySalary { get; set; }
public int numberOfPayCheck { get; set; }
}
Dependent class
public class Dependent
{
public string Name { get; set; }
public DependentType Type { get; set; }
}
POST Method
public IActionResult Post(dynamic employeeDependent1)
{
EmpDepViewMode employeeDependent = JsonConvert.DeserializeObject<EmpDepViewMode>(employeeDependent1);
return Ok(employeeDependent )
}
Any help would be appreciated
You have to fix action, you are trying to deserialize data the second time, since it was deserialized already automaticaly. And it doesn' t make much sense to make it dynamic since you use already EmpDepViewMode
public IActionResult Post(EmpDepViewMode employeeDependent)
{
return Ok(employeeDependent )
}
I have a problem that I cannot solve. I would like to transmit the deck created by the user to my API but the problem is that the card and the deck are two different entities in my database that's why I need to pass the information of the deck and the list card to my API to add them to my database.
my entity:
public class Card
{
public Card() {
this.Decks = new HashSet<Deck>();
}
public int Id { get; set; }
[Column(TypeName = "json")]
public string Content { get; set; }
[NotMapped]
public virtual ICollection<Deck> Decks { get; set; }
}
public class Deck
{
public Deck() {
this.Cards = new HashSet<Card>();
}
public int Id { get; set; }
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime CreateAt { get; set; }
public User User { get; set; }
[NotMapped]
public virtual ICollection<Card> Cards { get; set; }
}
public class Join
{
public int DeckId { get; set; }
public Deck Deck { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
my API:
[HttpPost]
public void Add ([FromBody] JsonObject request) {
}
the JSON:
{
"Deck":{
"Name": "",
"CreateAt": "2007-07-15",
"User": "null"
},
"Crads":[
{"content": {}},
{"content": {}}
]
}
Response:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|99e854f-4f63859a690203b6.",
"errors": {
"$.Deck.User": [
"The JSON value could not be converted to MTG_Deck.Models.User. Path: $.Deck.User | LineNumber: 4 | BytePositionInLine: 22."
]
}
}
I think it is better that in your API you receive a DTO that encapsulates the two classes you need (Deck and Card). Inside your method then take the information you need and save the entities correctly in the database.
public class AddDeckAndCardDTO
{
public Deck Deck { get; set; }
public List<Card> Card { get; set; }
}
And the method Add in the API
[HttpPost]
public void Add ([FromBody] AddDeckAndCardDTO request)
{
var card = request.Card;
var deck = request.Deck
// the code to mapped entities
// the code to save your entities
}
I'm using .NET Core and WebApi and I am trying to figure out what the url would look like to send an array of objects through.
For example
public class DataObject
{
public int id { get; set;}
public string name { get; set }
}
[HttpGet()]
public <ActionResult<string>> GetSomething(DataObject[] data))
{
//do something and return a string
}
what would the url look like to do this? Should I use FromQuery or FromRoute on data? On the HttpGet(), what should be in the parenthesis? "{data}" or something else?
Everything I can find so far has been on integer arrays or string arrays, but not complex arrays to a get call.
Update
Still not able to get this to work even though I'm sure the reply I have gotten should work. Here is some more code.
[Route("api/[controller]/[action]")]
[HttpGet()]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GridResult), (int)HttpStatusCode.OK)]
public async Task<ActionResult<GridResult>> GetGridData<TFilter1, TFilter2, TItem1>
([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1, [FromQuery] TFilter2[] TFilters2, [FromQuery] TItem1[] TSorts)
and finally the url that I have generated that throws a 404.
https://localhost:44366/api/grid/GetGridData/sessionID=598357390&details?NUMBER_OF_ROWS_FIRST_RETURNED=100&CURSOR_POSITION=0&RESULT_IN_SAXORDER=false&TERSERESPONSE=true&IsStaticList=true&GRID_TYPE=list&REQUEST_TYPE=LIST.DATA_ONLY.STORED&GRID_NAME=WUWP09&TFilters1[0].AliasName=PRO_CODE&TFilters1[0].Operator=%3D&TFilters1[0].SEQNUM=1&TFilters1[1].AliasName=APR_CLASS&TFilters1[1].Operator=%3D&Tsorts[1].SEQNUM=2&Tsorts[0].ALIAS_NAME=pvd_value&Tsorts[0].TYPE=ASC
Update 2
https://localhost:44366/api/grid/GetGridData?sessionID=598357390&details.NUMBER_OF_ROWS_FIRST_RETURNED=100&details.CURSOR_POSITION=0&details.RESULT_IN_SAXORDER=false&details.TERSERESPONSE=true&details.IsStaticList=true&details.GRID_TYPE=list&details.REQUEST_TYPE=LIST.DATA_ONLY.STORED&details.GRID_NAME=WUWP09&details.TAB_NAME&details.LOCALIZE_RESULT&details.USER_FUNCTION_NAME&details.TOTALRECORDS&details.RES_IsMoreRecords&details.RES_CURRENT_CURSOR_POSITION&TFilters1[0].AliasName=PRO_CODE&TFilters1[0].Operator=%3D&TFilters1[0].SEQNUM=1&TFilters1[1].AliasName=APR_CLASS&TFilters1[1].Operator=%3D&Tsorts[1].SEQNUM=2&Tsorts[0].ALIAS_NAME=pvd_value&Tsorts[0].TYPE=ASC
Update 3
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var _accessor = services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var config = new GridServices.Models.config();
Configuration.Bind("Connections", config);
services.AddSingleton(config);
services.AddSingleton(new Controllers.GridController(config));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
GridController
namespace EAMWebApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class GridController : ControllerBase
{
config Config { get; }
//private readonly LinkGenerator _linkGenerator;
public GridController(config config)
{
config = Config;
//_linkGenerator = linkGenerator;
}
[HttpGet()]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GridResult), (int)HttpStatusCode.OK)]
public async Task<ActionResult<GridResult>> GetGridData<TFilter1, TFilter2, TItem1>
([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1 = null, [FromQuery] TFilter2[] TFilters2 = null, [FromQuery] TItem1[] TSorts = null)
{//Do something}
}
GridDetails
namespace GridServices.Models
{
public class GridDetails
{
public string GRID_NAME { get; set; }
public string NUMBER_OF_ROWS_FIRST_RETURNED { get; set; }
public string CURSOR_POSITION { get; set; }
public string TAB_NAME { get; set; }
public string RESULT_IN_SAXORDER { get; set; }
public string TERSERESPONSE { get; set; }
public string LOCALIZE_RESULT { get; set; }
public string USER_FUNCTION_NAME { get; set; }
public string TOTALRECORDS { get; set; }
public bool RES_IsMoreRecords { get; set; }
public bool IsStaticList { get; set; }
public string GRID_TYPE { get; set; }
public string REQUEST_TYPE { get; set; }
public string RES_CURRENT_CURSOR_POSITION { get; set; }
}
}
MultiAddOnFilter
public class MultiAddOnFilter
{
public string ALIAS_NAME { get; set; }
public string OPERATOR { get; set; }
public string OPERATORSpecified { get; set; }
public string VALUE { get; set; }
public string LPAREN { get; set; }
public string RPAREN { get; set; }
public string JOINER { get; set; }
public string JOINERSpecified { get; set; }
public string SEQNUM { get; set; }
public MultiAddOnFilter(string _ALIAS_NAME, string _OPERATOR, string _VALUE)
{
ALIAS_NAME = _ALIAS_NAME;
OPERATOR = _OPERATOR;
OPERATORSpecified = "true";
VALUE = _VALUE;
}
}
Sorts
namespace GridServices.Models
{
public class Sort
{
public string ALIAS_NAME { get; set; }
public string TYPE { get; set; }
public string TYPESpecified { get; set; }
public Sort(string _ALIAS_NAME, string _TYPE)
{
ALIAS_NAME = _ALIAS_NAME;
TYPE = _TYPE;
TYPESpecified = "true";
}
}
}
what would the url look like to do this?
It should be something like the following:
GET /Somecontroller/GetSomething?data[0].id=1&data[0].name=nameA&data[1].id=2&data[1].name=nameB&data[2].id=3&data[2].name=nameC
This payload is almost the same as you post in application/x-www-form-urlencoded format, except you'll send it as a querystring.
[Edit]
If one of those items were to be empty, would I have to pass %00 to it to indicate a null value?
Let's say you have such an object array:
data = [
{
"id": 1,
"name": "nameA"
},
{
"id": 2,
"name": null
},
{
"id": 3,
"name": "nameC"
}
]
Note the data[1].name==null. you don't have to specify data[1].name :
?data[0].id=1&data[0].name=nameA&data[1].id=2&data[2].id=3&data[2].name=nameC
If the whole data[1] item is null, just adjust the index of data[2] to data[1]:
data[0].id=1&data[0].name=nameA&data[1].id=3&data[1].name=name
Or you could add an empty field for this item:
?data[0].id=1&data[0].name=nameA&data[1].id=&data[2].id=3&data[2].name=nameC
What if the whole DataObject was null? /GetSomething?data=%00 ?
you don't have to specify /GetSomething?data=%00, just send a request to /GetSomething?, and then you'll get an empty array.
[Edit2]
There're two reasons that always routes you to a 404 result:
You're registering your GridController as a singleton. MVC will register controllers automatically (as a scoped service). Just remove that line :
services.AddSingleton(new Controllers.GridController(config));
Your controller action of GetGridData<TFilter1, TFilter2, TItem1> is a generic method. It won't work by default. There's already a thread on SO talking about this. I would also suggest you use a specific GridFilter type for each method. If you find yourself repeating the same logic, you could put your generic method into a parent MySupperGridBaseController<TFilter1, TFilter2, TItem1> class, something as below:
public class MySupperGridBaseController<TFilter1, TFilter2, TItem1> : ControllerBase
{
public async Task<ActionResult<GridResult>> GetGridData
([FromQuery]string sessionID, [FromQuery] GridDetails details, [FromQuery] TFilter1[] TFilters1 = null, [FromQuery] TFilter2[] TFilters2 = null, [FromQuery] TItem1[] TSorts = null)
{
...
}
}
// now we could reuse the same logic inherited from parent
public class GridController : MySupperGridBaseController<MultiAddOnFilter, MultiAddOnFilter, Sort>
{
}
I have the following WebCleint to call a Restful web service inside my .net console application:-
try
{
using (WebClient wc = new WebClient())
{
wc.Encoding = Encoding.UTF8;
string url = "https://*****/paged?hapikey=*********&properties=website&properties=i_scan&limit=2";//web service url
string tempurl = url.Trim();
var json = wc.DownloadString(tempurl);//get the json
Marketing ipfd = JsonConvert.DeserializeObject<Marketing>(json);//deserialize
}
}
catch (Exception e)
{
//code goes here..
}
where i am using JSON.Net to Deserialize the json object, which will be as follow:-
{
"has-more": true,
"offset": 622438650,
"companies": [
{
"portalId": *******,
"companyId": *****,
"isDeleted": false,
"properties": {
"website": {
"value": "****.net",
"timestamp": 1520938239457,
"source": "CALCULATED",
"sourceId": null,
"versions": [
{
"name": "website",
"value": "*****.net",
"timestamp": 1520938239457,
"source": "CALCULATED",
"sourceVid": [
731938234
]
}
]
}
},
"additionalDomains": [],
"stateChanges": [],
"mergeAudits": []
},
{
"portalId": ******,
"companyId": ******,
"isDeleted": false,
"properties": {
"website": {
"value": "****.***.***",
"timestamp": 1512488590073,
"source": "CALCULATED",
"sourceId": null,
"versions": [
{
"name": "website",
"value": "****.***8.****",
"timestamp": 1512488590073,
"source": "CALCULATED",
"sourceVid": []
}
]
},
"i_scan": {
"value": "Yes",
"timestamp": 1543409493459,
"source": "******",
"sourceId": "**************",
"versions": [
{
"name": "i_scan",
"value": "Yes",
"timestamp": 1543409493459,
"sourceId": *****",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "******"
}
]
}
},
"additionalDomains": [],
"stateChanges": [],
"mergeAudits": []
}
]
}
Here are my classes:-
public class Marketing
{
public Companies companies { get; set; }
}
public class Companies
{
public IList<string> companyId { get; set; }
public IList<Properties> properties { get; set; }
}
public class Properties
{
public IList<Website> website { get; set; }
public IList<I_Scan> i_scan { get; set; }
}
public class Website
{
public string value { get; set; }
}
public class i_Scan
{
public string value { get; set; }
}
but currently i am getting this exception, when i try to de-serialize the JSON object:-
Newtonsoft.Json.JsonSerializationException was caught
HResult=-2146233088
Message=Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MMarketing.Companies' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'companies', line 1, position 49.
Source=Newtonsoft.Json
StackTrace:
so i am not sure why JSON.NET is unable to do the Deserialize correctly, as in my case the classes are compatible with the returned json object??
At a first glance it looks like you switched two properties in Making them a List and vice versa.
public class Marketing
{
public List<Companies> companies { get; set; }
}
Is "companies": [ in the json, while "companyId": *****, is the id as a string, not array. Properties is not an array also, but the property versions of properties is.
public class Companies
{
public string companyId { get; set; }
public Properties properties { get; set; }
}
If I'm coming to json blind I like to use http://json2csharp.com/ to generate my class structure for me
public class Version
{
public string name { get; set; }
public string value { get; set; }
public object timestamp { get; set; }
public string source { get; set; }
public List<object> sourceVid { get; set; }
}
public class Website
{
public string value { get; set; }
public object timestamp { get; set; }
public string source { get; set; }
public object sourceId { get; set; }
public List<Version> versions { get; set; }
}
public class Version2
{
public string name { get; set; }
public string value { get; set; }
public long timestamp { get; set; }
public int sourceId { get; set; }
public string source { get; set; }
public List<object> sourceVid { get; set; }
public int requestId { get; set; }
}
public class IScan
{
public string value { get; set; }
public long timestamp { get; set; }
public int source { get; set; }
public int sourceId { get; set; }
public List<Version2> versions { get; set; }
}
public class Properties
{
public Website website { get; set; }
public IScan i_scan { get; set; }
}
public class Company
{
public int portalId { get; set; }
public int companyId { get; set; }
public bool isDeleted { get; set; }
public Properties properties { get; set; }
public List<object> additionalDomains { get; set; }
public List<object> stateChanges { get; set; }
public List<object> mergeAudits { get; set; }
}
public class Marketing
{
public bool has_more { get; set; }
public int offset { get; set; }
public List<Company> companies { get; set; }
}
var result = JsonConvert.DeserializeObject<Marketing>(json);
I'm trying to parse a nested JSON string returned from the GCM (Google Could Messaging) server using VB.NET. The JSON string looks like this:
{ "multicast_id": 216,
"success": 3,
"failure": 3,
"canonical_ids": 1,
"results": [
{ "message_id": "1:0408" },
{ "error": "Unavailable" },
{ "error": "InvalidRegistration" },
{ "message_id": "1:1516" },
{ "message_id": "1:2342", "registration_id": "32" },
{ "error": "NotRegistered"}
]
}
I would like to get the results array in the above string.
I found the following example helpful, example but it does not show how to get to the nested parts, specifically message_id, error and registration_id inside the results array.
Thanks
I'll give an answer using c# and Json.net
var jobj = JsonConvert.DeserializeObject<Response>(json);
You can also use JavaScriptSerializer
var jobj2 = new JavaScriptSerializer().Deserialize<Response>(json);
public class Result
{
public string message_id { get; set; }
public string error { get; set; }
public string registration_id { get; set; }
}
public class Response
{
public int multicast_id { get; set; }
public int success { get; set; }
public int failure { get; set; }
public int canonical_ids { get; set; }
public List<Result> results { get; set; }
}