Why am I getting an InvalidCastException with competing Newtonshoft.Json.Linq.[JArray,JObject] with very similar code/data?

StackOverflow https://stackoverflow.com/questions/21247110

Question

This code works fine - returns the single record that matches the REST query:

Popul8TheGrid("http://localhost:28642/api/subdepartments/1/10");

private void Popul8TheGrid(string URIToPass)
{
    try
    {
        dataGridView1.DataSource = GetRESTData(URIToPass);
    }
    catch (WebException webex)
    {
        MessageBox.Show("Eek, a mousey-pooh! ({0})", webex.Message);
    }
}

private JArray GetRESTData(string uri)
{
    var webRequest = (HttpWebRequest) WebRequest.Create(uri);
    var webResponse = (HttpWebResponse) webRequest.GetResponse();
    var reader = new StreamReader(webResponse.GetResponseStream());
    string s = reader.ReadToEnd();
    return JsonConvert.DeserializeObject<JArray>(s);
}

However, this code, which also should return a single record:

private const string BASE_URI = "http://localhost:28642/api/";
. . .
string URIToPass = string.Format("{0}deliveryitems/{1}", BASE_URI, numericUpDownDeliveryItemId.Value);
Popul8TheGrid(URIToPass);

...fails, with "InvalidCastException was unhandled ... Message=Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'Newtonsoft.Json.Linq.JArray'".

Why might that be? The data returned from the first (working) snippet comes from an MS Access "database"

The data from the second (failing) snippet is from test data:

public DeliveryItemRepository() 
{
    // Just some bogus/test data for now
    Add(new DeliveryItem
    {
        Id = 1, InvoiceNumber = "123", UPC_PLU = "456", VendorItemId = "789", PackSize = 1, Description = "Something", Quantity = 5, Cost = 1.25M, 
        Margin = 0.25M, ListPrice = 1.50M, DepartmentNumber = 42, Subdepartment = "5"
    });

. . .

This is the Controller method; it works fine when entering the URI in a browser.

// Enter "http://localhost:28642/api/1"
[Route("api/DeliveryItems/{ID:int}")] 
public DeliveryItem GetDeliveryItemById(int ID)
{
    return _deliveryItemRepository.GetById(ID);
}

...but why that would matter, I know not...

UPDATE

Interestingly enough (perhaps I'm easily amused), this, OTOH, works:

MessageBox.Show(GetRESTScalarVal("http://localhost:28642/api/deliveries/1"));
. . .
private string GetRESTScalarVal(string uri)
{
    var client = new WebClient();
    return client.DownloadString(uri);
}

By "works," I mean it returns this:

enter image description here

So DownloadString() will even return an entire json "record" and my use of the word "Scalar" was misleading. Maybe I should have said "Single" instead, although that can be confusing, too, what with the data type of the same appellation.

The question still remains as to how I can populate a datagrid with a single json "record"

UPDATE 2

Oddly enough, if I use a different Controller method to get the one record, it works:

private void GetDeliveryItemById()
{
    //string uri = string.Format("deliveryitems/{0}", numericUpDownId.Value);
    string uri = string.Format("deliveryitems/{0}/1", numericUpDownId.Value);
    Popul8TheGrid(uri);
}

The commented out code is what blows up, whereas the other, with a provided const val of 1, works...kludgy, but it works.

UPDATE 3

Perhaps a clue/related to why it won't work when fetching one, but works otherwise, is this Repository code:

public SiteMapping GetById(int ID)
{
    return siteMappings.Find(p => p.Id == ID);
}

public IEnumerable<SiteMapping> GetRange(int ID, int CountToFetch)
{
    return siteMappings.Where(i => i.Id >= ID).Take(CountToFetch);
}

If GetById() is called with an ID that exists, it works; if one is passed that doesn't exist, though, it fails with, "InvalidOperationException was unhandled by user code . . . Message=Sequence contains no matching element"

Calling GetRange() works robustly - if passed a bogus pair of vals (no records), it simply shrugs its shoulders, rather than getting the old bulgy eye and screaming maniacally.

Changing it to so (see Simon Whitehead's answere here) works:

public SiteMapping GetById(int ID)
{
    var entity = siteMappings.Find(p => p.Id == ID);
    return entity == null ? null : entity;
}

So trying to find by a particular ID is fragile; trying to find by ID + Count works just fine. Why, I (still) don't know...

Was it helpful?

Solution

This may be somewhat kludgy, but it works:

private JArray GetRESTData(string uri)
{
    try
    {
        var webRequest = (HttpWebRequest)WebRequest.Create(uri);
        var webResponse = (HttpWebResponse)webRequest.GetResponse();
        var reader = new StreamReader(webResponse.GetResponseStream());
        string s = reader.ReadToEnd();
        return JsonConvert.DeserializeObject<JArray>(s);
    }
    catch // This method crashes if only one json "record" is found - try this:
    {
        try
        {
            MessageBox.Show(GetScalarVal(uri));
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    return null;
}

private string GetScalarVal(string uri)
{
    var client = new WebClient();
    return client.DownloadString(uri);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top