0

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

Community
  • 1
  • 1

2 Answers2

0

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; }
    }
}
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • you are correct in the URLs class and my excuses for answering this late. I had some busy days at my university. I made your changes shamefully it wasn't the correct fix and I somehow still got the same error. The problem I have is that I think Flurl thinks objectDescribe is an array. altho I could be entirely wrong not the best Programmer in the world and this is still all a bit new for me. or could it maybe be the List for the Recent items? hope this makes any sence sorry if it doesn't – Joey Kesselaar Apr 07 '17 at 08:15
  • The error now is at the very top level. Somewhere you're telling Flurl you want a list of `AccountItem`s rather than just one, and the API is returning just one. I think there's still relevant code you're not posting. What does `AccountGetAll.With().GetAll()` do exactly? I suspect that somewhere in there it's taking the type you pass it (`AccountItem` in this case) and telling Flurl you want a list of that type. – Todd Menier Apr 07 '17 at 12:49
  • BTW, the error does mention `objectDescribe` but that's a little misleading - I think that's just because it's the very first token encountered in the JSON. The part about not being able to deserialize to ``IEnumerable`1[InHollandCRMAPI.Models.AccountItem]`` is a lot more telling. – Todd Menier Apr 07 '17 at 12:53
  • thank you for being so helpfull and sorry for me answering this late all of the time, i'll try and post my code path my get call does and maybe that'll help you helping me. – Joey Kesselaar Apr 10 '17 at 07:24
  • added the code now already @ToddMenier this is where it goes past if I debug trough the code – Joey Kesselaar Apr 10 '17 at 07:50
-1

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