- Home /
Deserializing a JSON file yields 0 when referencing the variables in another script
I am having trouble with an API call response where when I try to reference a variable from the JSON data, it returns 0, even though the data received is not 0. I'm using Newtonsoft.Json to deserialize it.
This is the model that I use as a reference for handling the JSON:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
namespace OptionsChainResponse
{
[System.Serializable]
public class Greeks
{
public float delta;
public float gamma;
public float theta;
public float vega;
public float rho;
public float phi;
public float bid_iv;
public float mid_iv;
public float ask_iv;
public float smv_vol;
public string updated_at;
}
[System.Serializable]
public class Quote
{
public string symbol;
public string description;
public string exch;
public string type;
public float last;
public float change;
public int volume;
public float open;
public float high;
public float low;
public float close;
public float bid;
public float ask;
public string underlying;
public float strike;
public Greeks greeks;
public float change_percentage;
public int average_volume;
public int last_volume;
public long trade_date;
public float prevclose;
public float week_52_high;
public float week_52_low;
public int bidsize;
public string bidexch;
public long bid_date;
public int asksize;
public string askexch;
public long ask_date;
public int open_interest;
public int contract_size;
public string expiration_date;
public string expiration_type;
public string option_type;
public string root_symbol;
}
[System.Serializable]
public class Quotes
{
public Quote quote;
}
[System.Serializable]
public class Root
{
public Quotes quotes;
}
}
As I said, when I reference any of the above variables in another script, they return a value of 0, and I cannot figure out why. The portion of the script that I am running into problems with is as below:
IEnumerator GetOptionChainData()
{
ExpirationDate = (Expiration_Year.ToString() + Expiration_Month.ToString() + Expiration_Day.ToString());
AnalyzeButton1.text = "Loading...";
AnalyzeButton2.text = "Loading...";
string uri = "https://api.tradier.com/v1/markets/quotes?symbols=" + Ticker + ExpirationDate + PutOrCallString + StrikePriceString + "&greeks=true";
using (UnityWebRequest request = UnityWebRequest.Get(uri))
{
request.SetRequestHeader("Authorization", "Bearer " + TradierAPIKey);
request.SetRequestHeader("Accept", "application/json");
yield return request.SendWebRequest();
if ((request.isNetworkError) || (request.isHttpError))
{
AnalyzeButton1.text = "Error!";
AnalyzeButton2.text = "Error!";
Debug.Log("Quote Error:" + request.error);
}
else
{
if (request.isDone)
{
var opt1 = JsonConvert.DeserializeObject<OptionsChainResponse.Greeks>(request.downloadHandler.text);
if (request.downloadHandler.text != null && request.downloadHandler.text != "")
{
ImpliedVolatility = opt1.mid_iv; //Implied volatility of options
Theta = opt1.theta; //Theta of the contract
Gamma = opt1.gamma; //Gamma of the contract
}
var opt2 = JsonConvert.DeserializeObject<OptionsChainResponse.Quote>(request.downloadHandler.text);
if (request.downloadHandler.text != null && request.downloadHandler.text != "")
{
OptionsChainResponse = request.downloadHandler.text;
//MaxPain; //Maximum Pain Price
//MaxPainString;
OpenInterest = opt2.open_interest; //Open interest of options
Bid = opt2.bid; //Bid price of the contract
Mid = ((opt2.bid + opt2.ask) / 2); //Mid price of the contract
Ask = opt2.ask; //Ask price of the contract
if (Price > StrikePrice)
{
ITMText.SetActive(true);
OTMText.SetActive(false);
}
if (Price <= StrikePrice)
{
OTMText.SetActive(true);
ITMText.SetActive(false);
}
Debug.Log(OptionsChainResponse);
}
}
}
}
}
I have no idea what I'm doing wrong at this point, as when I Debug.Log the response that I get, all of the data is there properly, so it has to be something with deserializing the JSON data. I'm not sure if it would help here, but the response from the API would look as follows:
{
"quotes": {
"quote": {
"symbol": "AAPL220121P00167500",
"description": "AAPL Jan 21 2022 $167.50 Put",
"exch": "Z",
"type": "option",
"last": 2.45,
"change": 0.00,
"volume": 0,
"open": null,
"high": null,
"low": null,
"close": null,
"bid": 2.3,
"ask": 2.47,
"underlying": "AAPL",
"strike": 167.5,
"greeks": {
"delta": -0.484713,
"gamma": 0.0999226,
"theta": -0.392862,
"vega": 0.0498053,
"rho": 0.00441615,
"phi": -0.0044981,
"bid_iv": 0.335705,
"mid_iv": 0.337713,
"ask_iv": 0.339721,
"smv_vol": 0.332,
"updated_at": "2022-01-19 20:33:07"
},
"change_percentage": 0.00,
"average_volume": 0,
"last_volume": 50,
"trade_date": 1642625993207,
"prevclose": 2.45,
"week_52_high": 0.0,
"week_52_low": 0.0,
"bidsize": 0,
"bidexch": "W",
"bid_date": 1642665600000,
"asksize": 0,
"askexch": "W",
"ask_date": 1642665600000,
"open_interest": 18476,
"contract_size": 100,
"expiration_date": "2022-01-21",
"expiration_type": "standard",
"option_type": "put",
"root_symbol": "AAPL"
}
}
}
Any help here would be immensely appreciated as I've tried searching for an answer but I cannot find anything about this specific problem. Thank you for your time.
Answer by Bunny83 · Jan 21 at 11:44 AM
So I have officially double-checked everything
Well, you have double checked everything except the fact that the class(es) you use for the deserialization does not represent your json data (assuming the information you provided above is correct).
So first establish some facts:
According to your explanation
request.downloadHandler.text
contains the given json text that you posted at the end of your question.You then try to deserialize that very same text twice, once in line 22 and once again in line 29 of your middle snippet.
In the first case you try to deserialize the given json text into a
Greeks
instance, in the second case you try to deserialize the very same text into aQuote
instance.
Well, here are your problems:
The json text you posted neither represents a "Greeks" nor a "Quote" instance. So it's kinda obvious that all the fields you're interested in are not loaded properly. Just take a look at the json text. The object in the json only contains a single field which is called "quotes". Your "Greeks" class does not even have that field, so it is ignored. What else do we have in the object? well, nothing else. Therefore the deserialized instance you get back are just empty initialized objects which got no data assigned from the given json text, because non of the fields match anything in that class. The same is of course true for your Quote class.
The funny thing is, you already have the other necessary classes in your project, but you don't use them. I assume you used a json to C# converter to create those classes? Well all those classes simply got named after the field name they are used in, except for the top object which is simply called "Root". This is the class that actually represents your json text and this is the class you have to use when you deserialize your json text. The Root class does have a field called "quotes". The object behind the "quotes" field contains just a single field called "quote". This sub object contains all the fields specified in the "Quote" class. The Quote class has one subfield called "greeks". This field is of your "Greeks" type and the fields in this sub object match the fields of your Greeks class.
ps: there's no need for the isDone check. Above you speculated wether that would somehow "wait" for the completion, it does not. You wait for the completion in line 11 through the yield return
line. After that it's just a matter of telling if the request finished successfully or not. You handle the error cases through the if statement in line 12. So the "else" block is the success case. Checking for "isDone" is completely redundant here.
ps: If this is an actual API, I have the strong feeling that something may got wrong on the server side. a field named "quotes" indicates that this most likely is an array / collection of multiple "quote" objects. However currently your structure does only ever return a single one, since all elements in your json are objects, not arrays. Maybe that's the intended use, if it is, it would be a strange design. If it is not you will run into issues sooner or later. We don't have enough information to say anything more about this specific case. I just mentioned it as a warning.
After fixing the model reference, the last thing was to handle the nullable types and it all started working properly. Thank you very much for pointing me in the right direction.
Answer by mistergreen2016 · Jan 20 at 10:24 PM
Wait until you finish loading the JSON, then deserialize. The process is async not sync.
Would that not be handled by line 20: if (request.isDone)? How would I accomplish this otherwise?
Thank you for the reply
Not sure, try debug inside the request.isDone block.
The weirdest part is that I've had other API calls structured the same way, that all deserialize perfectly and display their values correctly. So I don't think it's that the JSON hasn't finished loading back, I think it's the multiple classes that messes things up as I'm not sure how to deserialize the JSON properly with multiple classes as shown in the API response.
So I have officially double-checked everything, and the JSON file is fully loaded at the time I am trying to deserialize it. My code correctly references the variables in the classes that the deserializer references as a model. Everything is smooth and without errors, except that the data is never assigning the values to the variables properly. They remain their default value, 0. I'm not sure what to do from this point.
Double-check your JsonConvert.DeserializeObject. Maybe it has trouble with nested classes or namespace. It happens. I think the newer Unity jsonUntility can do it.
Your answer
Follow this Question
Related Questions
deserialize a json web api on top of the class or inside the start function 0 Answers
Json Parsing iOS with depth of json is 6. 0 Answers
JSON Deserialization 2 Answers
fromjson array ends up null 0 Answers