I’ve been spinning my wheels on this issue for a couple days so I thought I’d better ask the forums.
I am using the ShopifySharp C# library to make API calls and I’m having an issue deserializing a product.
My app needs to grab a certain time period’s worth of orders, then loop through each order’s line item to retrieve products. Essentially my process will be dynamically building product review reminder emails in Bronto.
Anyway, the part that’s not working is the following:
Here’s my method that makes the call:
public List<T> CallService<T>(string query)
{
var data = new List<T>();
var fullyEscapedUri = _ShopUri.AbsoluteUri.EndsWith("/") ? _ShopUri : new Uri(_ShopUri + "/");
var uri = new Uri(fullyEscapedUri, typeof(T).Name);
if (!string.IsNullOrWhiteSpace(query))
{
var uriBuilder = new UriBuilder(uri) { Query = query };
uri = uriBuilder.Uri;
}
string url = String.Format("{0}{1}", _ShopUri, query);
// The WebRequest method will compile for .net4.5 but not 1.4.
var request = WebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/json";
string base64EncodedUsernameAndPassword = string.Format("{0}:{1}", _Username, _Password);
string authHeader = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes(base64EncodedUsernameAndPassword)));
request.Headers["Authorization"] = authHeader;
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
var jsonObj = JsonConvert.DeserializeObject(json) as JObject;
var jsonArray = jsonObj[_ObjectType] as JArray; // code is crashing here for product json. Order json is being parsed fine.
foreach (var jsonEmp in jsonArray)
{
var obj = JsonConvert.DeserializeObject<T>(jsonEmp.ToString());
data.Add(obj);
}
}
}
}
return data;
}
When it hits the products, it’s crashing at this line, leaving a null jsonArray JArray:
var jsonArray = jsonObj[_ObjectType] as JArray; // code is crashing here for product json. Order json is being parsed fine.
I’ve verified all the variables going into this function are fine (query URL, ObjectType, Username, Password…..). I’ve verified that the code right before the problem line about is returning the expected JSON string.
Here is the JSON being returned – I’m only grabbing two fields:
{
"product": {
"handle": "my-product-handle",
"images": [
{
"id": 11112222333444,
"product_id": 1234567890,
"position": 1,
"created_at": "2018-12-11T16:05:26-06:00",
"updated_at": "2018-12-11T16:05:26-06:00",
"alt": null,
"width": 800,
"height": 800,
"src": "https://cdn.shopify.com/some_url",
"variant_ids": [],
"admin_graphql_api_id": "gid://shopify/ProductImage/some_other_url"
}
]
}
}
and here are my relevant C# classes:
public class Product : ShopifyObject
{
/// <summary>
/// The name of the product. In a shop's catalog, clicking on a product's title takes you to that product's page.
/// On a product's page, the product's title typically appears in a large font.
/// </summary>
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// The description of the product, complete with HTML formatting.
/// </summary>
[JsonProperty("body_html")]
public string BodyHtml { get; set; }
/// <summary>
/// The name of the vendor of the product.
/// </summary>
[JsonProperty("vendor")]
public string Vendor { get; set; }
/// <summary>
/// A categorization that a product can be tagged with, commonly used for filtering and searching.
/// </summary>
[JsonProperty("product_type")]
public string ProductType { get; set; }
/// <summary>
/// The date and time when the product was created. The API returns this value in ISO 8601 format.
/// </summary>
[JsonProperty("created_at", DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTimeOffset? CreatedAt { get; set; }
/// <summary>
/// A human-friendly unique string for the Product automatically generated from its title.
/// They are used by the Liquid templating language to refer to objects.
/// </summary>
[JsonProperty("handle")]
public string Handle { get; set; }
/// <summary>
/// The date and time when the product was last modified. The API returns this value in ISO 8601 format.
/// </summary>
[JsonProperty("updated_at", DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTimeOffset? UpdatedAt { get; set; }
/// <summary>
/// The date and time when the product was published. The API returns this value in ISO 8601 format.
/// Set to NULL to unpublish a product
/// </summary>
[JsonProperty("published_at", DefaultValueHandling = DefaultValueHandling.Include, NullValueHandling = NullValueHandling.Include)]
public DateTimeOffset? PublishedAt { get; set; }
/// <summary>
/// The suffix of the liquid template being used.
/// By default, the original template is called product.liquid, without any suffix.
/// Any additional templates will be: product.suffix.liquid.
/// </summary>
[JsonProperty("template_suffix")]
public object TemplateSuffix { get; set; }
/// <summary>
/// A categorization that a product can be tagged with, commonly used for filtering and searching.
/// Each comma-separated tag has a character limit of 255.
/// </summary>
[JsonProperty("tags")]
public string Tags { get; set; }
/// <summary>
/// The sales channels in which the product is visible.
/// </summary>
[JsonProperty("published_scope")]
public string PublishedScope { get; set; }
/// <summary>
/// A list of variant objects, each one representing a slightly different version of the product.
/// For example, if a product comes in different sizes and colors, each size and color permutation (such as "small black", "medium black", "large blue"), would be a variant.
/// To reorder variants, update the product with the variants in the desired order.The position attribute on the variant will be ignored.
/// </summary>
[JsonProperty("variants")]
public IEnumerable<ProductVariant> Variants { get; set; }
/// <summary>
/// Custom product property names like "Size", "Color", and "Material".
/// Products are based on permutations of these options.
/// A product may have a maximum of 3 options. 255 characters limit each.
/// </summary>
[JsonProperty("options")]
public IEnumerable<ProductOption> Options { get; set; }
/// <summary>
/// A list of image objects, each one representing an image associated with the product.
/// </summary>
[JsonProperty("images")]
public IEnumerable<ProductImage> Images { get; set; }
}
and ProductImage:
public class ProductImage : ShopifyObject
{
/// <summary>
/// The id of the product associated with the image.
/// </summary>
[JsonProperty("product_id")]
public long? ProductId { get; set; }
/// <summary>
/// The order of the product image in the list. The first product image is at position 1 and is the "main" image for the product.
/// </summary>
[JsonProperty("position")]
public int? Position { get; set; }
/// <summary>
/// The date and time when the product image was created. The API returns this value in ISO 8601 format.
/// </summary>
[JsonProperty("created_at")]
public DateTimeOffset? CreatedAt { get; set; }
/// <summary>
/// The date and time when the product image was last modified. The API returns this value in ISO 8601 format.
/// </summary>
[JsonProperty("updated_at")]
public DateTimeOffset? UpdatedAt { get; set; }
/// <summary>
/// Specifies the alt tag of the product image.
/// </summary>
[JsonProperty("alt")]
public object Alt { get; set; }
/// <summary>
/// Specifies the width of the product image.
/// </summary>
[JsonProperty("width")]
public int? width { get; set; }
/// <summary>
/// Specifies the height of the product image.
/// </summary>
[JsonProperty("height")]
public int? height { get; set; }
/// <summary>
/// Specifies the location of the product image.
/// </summary>
[JsonProperty("src")]
public string Src { get; set; }
/// <summary>
/// An array of variant ids associated with the image.
/// </summary>
[JsonProperty("variant_ids")]
public IEnumerable<long> VariantIds { get; set; }
/// <summary>
/// An array of variant ids associated with the image.
/// </summary>
[JsonProperty("admin_graphql_api_id")]
public string AdminGraphQlApiId { get; set; }
}
Any ideas?
Ben
2
Answers
I wound up just using the products.json endpoint rather than the one to retrieve a single one by ID (product/{product ID}.json). Then I'm grabbing the order from the entire serialized list of products.
IMO you wouldn’t need classes for a few items. i will give you an example of my code snippet.
you can get any product item information from json using the array index.
hope this pushes you somewhat in the right direction. or at least gives you something to think about.