Reputation: 21
I am a newbie to JSON and I have some JSON that I trying to parse with C#.
I have tried making a class to represent by data, but the names of my properties are based on times so I’d have to hard code my data contracts. I’ve tried JSON.NET and LINQ to sort through the data, but keep getting null values due to the strange object/property.
Again I am very new to JSON so I’m sure there is a simple fix to this, I’m just not sure how to ask the question correctly. Thank you for your help.
Below is a small sample of JSON I am struggling to parse. Again thanks.
{
"Meta Data": {
"1. Information": "Intraday (1min) prices and volumes",
"2. Symbol": "MU",
"3. Last Refreshed": "2017-05-30 16:00:00",
"4. Interval": "1min",
"5. Output Size": "Full size",
"6. Time Zone": "US/Eastern"
},
"Time Series (1min)": {
"2017-05-30 16:00:00": {
"1. open": "30.7200",
"2. high": "30.7300",
"3. low": "30.7000",
"4. close": "30.7000",
"5. volume": "1390302"
},
"2017-05-30 15:59:00": {
"1. open": "30.7750",
"2. high": "30.7800",
"3. low": "30.7200",
"4. close": "30.7250",
"5. volume": "380134"
}
}
}
Note that the "Time Series"
properties come in 1min, 5min, 15min, 30min, 60min intervals, i.e. "Time Series (##min)"
for various ##min
.
Upvotes: 2
Views: 2036
Reputation: 5302
To achieve the effect of having a dynamic name for the two properties in TimeSeries
, you could parse manually the json tree (with Newtonsoft Json
library):
The deserialization domain:
public class MetaData
{
public string Information { get; set; }
public string Symbol { get; set; }
public DateTime LastRefreshed { get; set; }
public string Interval { get; set; }
public string OutputSize { get; set; }
public string TimeZone { get; set; }
}
public class TimeSeriesInfos
{
public double Open { get; set; }
public double High { get; set; }
public double Low { get; set; }
public double Close { get; set; }
public double Volume { get; set; }
}
public class TimeSeries
{
public TimeSeriesInfos T1 { get; set; }
public TimeSeriesInfos T2 { get; set; }
}
public class RootObject
{
public MetaData MetaData { get; set; }
public TimeSeries TimeSeries { get; set; }
}
And then deserialize it like this:
var jsonObjectTree = JsonConvert.DeserializeObject<JObject>(
File.ReadAllText("exampleFile.json"));
const string metaDataName = "Meta Data";
const string timeSeriesName = "Time Series (1min)";
const string openName = "1. open";
const string highName = "2. high";
const string lowName = "3. low";
const string closeName = "4. close";
const string volumeName = "5. volume";
// You can obtain dynamically those two properties
string t1Name = "2017-05-30 16:00:00";
string t2Name = "2017-05-30 15:59:00";
var deserializedObject = new RootObject()
{
MetaData = new MetaData()
{
Information = jsonObjectTree[metaDataName]["1. Information"].Value<string>(),
Symbol = jsonObjectTree[metaDataName]["2. Symbol"].Value<string>(),
LastRefreshed = DateTime.ParseExact(jsonObjectTree[metaDataName]["3. Last Refreshed"].Value<string>(), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
Interval = jsonObjectTree[metaDataName]["4. Interval"].Value<string>(),
OutputSize = jsonObjectTree[metaDataName]["5. Output Size"].Value<string>(),
TimeZone = jsonObjectTree[metaDataName]["6. Time Zone"].Value<string>()
},
TimeSeries = new TimeSeries()
{
T1 = new TimeSeriesInfos()
{
Open = jsonObjectTree[timeSeriesName][t1Name][openName].Value<double>(),
High = jsonObjectTree[timeSeriesName][t1Name][highName].Value<double>(),
Low = jsonObjectTree[timeSeriesName][t1Name][lowName].Value<double>(),
Close = jsonObjectTree[timeSeriesName][t1Name][closeName].Value<double>(),
Volume = jsonObjectTree[timeSeriesName][t1Name][volumeName].Value<double>()
},
T2 = new TimeSeriesInfos()
{
Open = jsonObjectTree[timeSeriesName][t2Name][openName].Value<double>(),
High = jsonObjectTree[timeSeriesName][t2Name][highName].Value<double>(),
Low = jsonObjectTree[timeSeriesName][t2Name][lowName].Value<double>(),
Close = jsonObjectTree[timeSeriesName][t2Name][closeName].Value<double>(),
Volume = jsonObjectTree[timeSeriesName][t2Name][volumeName].Value<double>()
}
}
};
Upvotes: 0
Reputation: 117344
You would like to deserialize your JSON series to some c# type, however it's not obvious how to do so since the JSON objects have both fixed and variable property names, none of which correspond to valid c# identifiers. Specifically:
Your root object has a property "Meta Data"
that corresponds to a JSON object with a collection of string key/value pairs. Following the answers from this question you can bind this to a dictionary property:
[JsonProperty("Meta Data")]
public Dictionary<string, string> MetaData { get; set; }
In addition, your root object has an arbitrary set of properties with names like "Time Series (##min)" for various ##min
with a fixed schema that corresponds to a Dictionary<DateTime, Dictionary<string, decimal>>
. Because these properties have a fixed schema you cannot just use [JsonExtensionData]
as proposed in Deserialize json with known and unknown fields. Instead, you can use the converter TypedExtensionDataConverter<TObject>
from How to deserialize a child object with dynamic (numeric) key names? to deserialize your root object, making the time series property be as follows:
[JsonTypedExtensionData]
public Dictionary<string, Dictionary<DateTime, Dictionary<string, decimal>>> TimeSeries { get; set; }
Thus you can design your root object as follows:
[JsonConverter(typeof(TypedExtensionDataConverter<RootObject>))]
public class RootObject
{
public RootObject()
{
// Ensure dictionaries are allocated.
this.MetaData = new Dictionary<string, string>();
this.TimeSeries = new Dictionary<string, Dictionary<DateTime, Dictionary<string, decimal>>>();
}
[JsonProperty("Meta Data")]
public Dictionary<string, string> MetaData { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, Dictionary<DateTime, Dictionary<string, decimal>>> TimeSeries { get; set; }
}
With the TypedExtensionDataConverter<RootObject>
copied verbatim from this answer.
Sample fiddle.
Note that if the set of property names "1. open"
, "2. high"
, and so on for each time series time is fixed, you can use a predefined type similar to the T1
from @FrancescoB's answer instead of a Dictionary<string, decimal>
:
[JsonConverter(typeof(TypedExtensionDataConverter<RootObject>))]
public class RootObject
{
public RootObject()
{
// Ensure dictionaries are allocated.
this.MetaData = new Dictionary<string, string>();
this.TimeSeries = new Dictionary<string, Dictionary<DateTime, TimeSeriesData>>();
}
[JsonProperty("Meta Data")]
public Dictionary<string, string> MetaData { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, Dictionary<DateTime, TimeSeriesData>> TimeSeries { get; set; }
}
public class TimeSeriesData
{
[JsonProperty("1. open")]
public decimal Open { get; set; }
[JsonProperty("2. high")]
public decimal High { get; set; }
[JsonProperty("3. low")]
public decimal Low { get; set; }
[JsonProperty("4. close")]
public decimal Close { get; set; }
[JsonProperty("5. volume")]
public decimal Volume { get; set; }
}
Sample fiddle #2.
Upvotes: 1
Reputation: 5302
You can use this classes to deserialize that particular Json file, here I'm assuming that the two objects inside Time Series (1min)
will be have the same names in every json file. But considering that they are dates, I'm pretty sure the will be different each time you will download the json.
Just to give you a little idea of what you can do with Newtonsoft Json
attributes:
public class MetaData
{
[JsonProperty("1. Information")]
public string Information { get; set; }
[JsonProperty("2. Symbol")]
public string Symbol { get; set; }
[JsonProperty("3. Last Refreshed")]
public string LastRefreshed { get; set; }
[JsonProperty("4. Interval")]
public string Interval { get; set; }
[JsonProperty("5. Output Size")]
public string OutputSize { get; set; }
[JsonProperty("6. Time Zone")]
public string TimeZone { get; set; }
}
public class T1
{
[JsonProperty("1. Information")]
public string Open { get; set; }
[JsonProperty("2. high")]
public string High { get; set; }
[JsonProperty("3. low")]
public string Low { get; set; }
[JsonProperty("4. close")]
public string Close { get; set; }
[JsonProperty("5. volume")]
public string Volume { get; set; }
}
public class T2
{
[JsonProperty("1. Information")]
public string Open { get; set; }
[JsonProperty("2. high")]
public string High { get; set; }
[JsonProperty("3. low")]
public string Low { get; set; }
[JsonProperty("4. close")]
public string Close { get; set; }
[JsonProperty("5. volume")]
public string Volume { get; set; }
}
public class TimeSeries
{
[JsonProperty("2017-05-30 16:00:00")]
public T1 T1 { get; set; }
[JsonProperty("2017-05-30 15:59:00")]
public T2 T2 { get; set; }
}
public class RootObject
{
[JsonProperty("Meta Data")]
public MetaData MetaData { get; set; }
[JsonProperty("Time Series (1min)")]
public TimeSeries TimeSeries { get; set; }
}
Then, when you deserialize:
var deserializedObject = JsonConvert.DeserializeObject<RootObject>(
File.ReadAllText("exampleFile.json"));
If you can tell us something more about you json file, we could help you better.
Upvotes: 3
Reputation: 230
You can just try using JsonCovert as follow
string json = @"{
'Meta Data': {
'1. Information': 'Intraday (1min) prices and volumes',
'2. Symbol': 'MU',
'3. Last Refreshed': '2017-05-30 16:00:00',
'4. Interval': '1min',
'5. Output Size': 'Full size',
'6. Time Zone': 'US/Eastern'
},
'Time Series (1min)': {
'2017-05-30 16:00:00': {
'1. open': '30.7200',
'2. high': '30.7300',
'3. low': '30.7000',
'4. close': '30.7000',
'5. volume': '1390302'
},
'2017-05-30 15:59:00': {
'1. open': '30.7750',
'2. high': '30.7800',
'3. low': '30.7200',
'4. close': '30.7250',
'5. volume': '380134'
}
}
}";
var jsonConvertedData = JsonConvert.DeserializeObject(json);
This will parse json string to json object.
Upvotes: 0