问题
The title is exactly what I meant
All the questions on Stackoverflow asks for whether if the json data is an object or array but what I'm looking for is to see if I can find out if the array is an array of primitive types or objects.
Currently, i already can identify if its an array or not, just that I'm unable to convert if it is not an array of strings.
This code is wrapped in a for loop, where it is (var comArrEl in comArr), where comArr is an array of strings. This array stores something like "gesmes:Envelope:Cube:Cube:@currency="USD"
Basically we're trying to write a universal API wrapper here.
// Identify if its an array or an object
if (token is JArray)
{
try
{
// Parse the comArrEl to an integer for index access
if (int.TryParse(comArrEl, out int index))
{
// Pump in the array
var dataList = token.ToObject<List<object>>();
// Is it the last?
if (comArrEl != last)
{
// let's work it out
// update the token
if (index >= 0 && index < dataList.Count)
{
token = JToken.Parse(JsonConvert.SerializeObject(dataList[index]));
}
}
// Yes its the last
else
{
var property = dataList[index];
// Number checks
// Make sure the datalist element we're targetting contains a proper value.
if (decimal.TryParse(property, out decimal val))
{
// Update it
_currencyPairComponentService.UpdatePairValue(component.Id, val);
}
}
}
// Incorrect comArrEl.
else
{
return false;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
As you can see, the exception triggers on line 2.
Sample payload to code:
[
{
"@currency": "USD",
"@rate": "1.1354"
},
{
"@currency": "JPY",
"@rate": "128.31"
},
{
"@currency": "BGN",
"@rate": "1.9558"
},
{
"@currency": "CZK",
"@rate": "25.886"
},
{
"@currency": "DKK",
"@rate": "7.4630"
},
{
"@currency": "GBP",
"@rate": "0.88885"
},
{
"@currency": "HUF",
"@rate": "323.49"
},
{
"@currency": "PLN",
"@rate": "4.2826"
},
{
"@currency": "RON",
"@rate": "4.6528"
},
{
"@currency": "SEK",
"@rate": "10.1753"
},
{
"@currency": "CHF",
"@rate": "1.1328"
},
{
"@currency": "ISK",
"@rate": "139.40"
},
{
"@currency": "NOK",
"@rate": "9.6480"
},
{
"@currency": "HRK",
"@rate": "7.3990"
},
{
"@currency": "RUB",
"@rate": "75.8385"
},
{
"@currency": "TRY",
"@rate": "6.0453"
},
{
"@currency": "AUD",
"@rate": "1.5569"
},
{
"@currency": "BRL",
"@rate": "4.3692"
},
{
"@currency": "CAD",
"@rate": "1.5076"
},
{
"@currency": "CNY",
"@rate": "7.7848"
},
{
"@currency": "HKD",
"@rate": "8.8695"
},
{
"@currency": "IDR",
"@rate": "16344.08"
},
{
"@currency": "ILS",
"@rate": "4.2293"
},
{
"@currency": "INR",
"@rate": "80.0660"
},
{
"@currency": "KRW",
"@rate": "1264.39"
},
{
"@currency": "MXN",
"@rate": "23.2282"
},
{
"@currency": "MYR",
"@rate": "4.7165"
},
{
"@currency": "NZD",
"@rate": "1.6398"
},
{
"@currency": "PHP",
"@rate": "59.878"
},
{
"@currency": "SGD",
"@rate": "1.5520"
},
{
"@currency": "THB",
"@rate": "37.190"
},
{
"@currency": "ZAR",
"@rate": "15.6366"
}
]
回答1:
This method can dynamically iterate a JSON result, be it object or primitive. This method loops through an array called requestComponents , which defines the properties we want to obtain from the JSON payload.
It then breaks down each requestComponent string into an array (i.e. "gesmes:Envelope/Cube/Cube/Cube/0=>@rate") to allow us to go down the object/array. Sometimes its an array, sometimes its an object, we can easily go down that path. A custom "=>" syntax that only works for the last element of the requestComponent array will tell the code to retrieve that property. (sample: 0=>@currency // That will tell us to obtain the object of index 0, the "@currency" property)
Here's the code, do not focus on the inputs, but rather the logic. The main point here is to peek into the upcoming property to see if its an array or etc before processing it. So your target could be
object/array/object/array/array element index
or it could also be
array/object/array/object/property
voila
ResponseType is an Enum, RequestComponents is an object that contains the string to traverse (That's all that matters in that object tbh) and token is basically the json payload.
public bool Update(JToken token, ResponseType resType, IEnumerable<RequestComponent> requestComponents)
{
// For each component we're checking
foreach (var component in requestComponents)
{
var comArr = component.QueryComponent.Split("/"); // Split the string if its nesting
var last = comArr.LastOrDefault(); // get the last to identify if its the last
// Iterate the queryComponent Array
foreach (var comArrEl in comArr)
{
// Null check
if (comArrEl != null)
{
// CHECK CURRENT TYPE
// Identify if its an array or an object
if (token is JArray)
{
try
{
// Is it the last?
if (comArrEl != last)
{
// Parse the comArrEl to an integer for index access
if (int.TryParse(comArrEl, out int index))
{
// Pump in the array, treat it as anonymous.
var dataList = token.ToObject<List<JObject>>();
// let's work it out
// update the token
if (index >= 0 && index < dataList.Count)
{
// Traverse the array
token = JToken.Parse(JsonConvert.SerializeObject(dataList[index]));
}
}
}
// Yes its the last
else
{
// See if theres any property we need to refer to.
var comArrElArr = comArrEl.Split("=>");
if (int.TryParse(comArrElArr[0], out var index))
{
// Traverse first
var rawData = token.ToObject<List<JToken>>()[index];
// if its 1, we assume its just an array of a primitive type
if (comArrElArr.Length == 1)
{
// Retrieve the value.
var rawVal = rawData.ToString();
// https://stackoverflow.com/questions/23131414/culture-invariant-decimal-tryparse
var style = NumberStyles.Any;
if (ExponentHelper.IsExponentialFormat(rawVal))
{
style = NumberStyles.Float;
}
// If it is an exponent
if (decimal.TryParse(rawVal, style, CultureInfo.InvariantCulture,
out var val))
{
if (val > 0)
{
// Update it
_currencyPairComponentService.UpdatePairValue(component.Id, val);
}
}
}
// Oh no.. non-primitive...
else if (comArrElArr.Length == 2)
{
// Object-ify
var rawObj = JObject.Parse(rawData.ToString());
// Obtain the desired value
var rawVal = rawObj[comArrElArr[1]].ToString();
// As usual, update it
// https://stackoverflow.com/questions/23131414/culture-invariant-decimal-tryparse
var style = NumberStyles.Any;
if (ExponentHelper.IsExponentialFormat(rawVal))
{
style = NumberStyles.Float;
}
// If it is an exponent
if (decimal.TryParse(rawVal, style, CultureInfo.InvariantCulture,
out var val))
{
if (val > 0)
{
// Update it
_currencyPairComponentService.UpdatePairValue(component.Id, val);
}
}
}
else
{
// Invalid
return false;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
else if (token is JObject)
{
// Pump in the object
JObject obj = token.ToObject<JObject>();
// Is it the last?
if (comArrEl != last)
{
// let's work it out
// update the token
token = obj.SelectToken(comArrEl);
}
// Yes its the last
else
{
// See if theres any property we need to refer to.
var comArrElArr = comArrEl.Split("=>");
// Traverse first
var rawData = (string) obj.SelectToken(comArrElArr[0]);
if (rawData != null)
{
// if its 1, we assume its just an array of a primitive type
if (comArrElArr.Length == 1)
{
// Retrieve the value.
var rawVal = rawData.ToString();
// https://stackoverflow.com/questions/23131414/culture-invariant-decimal-tryparse
var style = NumberStyles.Any;
if (ExponentHelper.IsExponentialFormat(rawVal))
{
style = NumberStyles.Float;
}
// If it is an exponent
if (decimal.TryParse(rawVal, style, CultureInfo.InvariantCulture,
out var val))
{
if (val > 0)
{
// Update it
_currencyPairComponentService.UpdatePairValue(component.Id, val);
}
}
}
// Oh no.. non-primitive...
else if (comArrElArr.Length == 2)
{
// Object-ify
var rawObj = JObject.Parse(rawData.ToString());
// Obtain the desired value
var rawVal = rawObj[comArrElArr[1]].ToString();
// As usual, update it
// https://stackoverflow.com/questions/23131414/culture-invariant-decimal-tryparse
var style = NumberStyles.Any;
if (ExponentHelper.IsExponentialFormat(rawVal))
{
style = NumberStyles.Float;
}
// If it is an exponent
if (decimal.TryParse(rawVal, style, CultureInfo.InvariantCulture,
out var val))
{
if (val > 0)
{
// Update it
_currencyPairComponentService.UpdatePairValue(component.Id, val);
}
}
}
else
{
// Invalid
return false;
}
}
}
}
// iterate JValue like a JObject
else if (token is JValue)
{
// Pump in the object
JObject obj = token.ToObject<JObject>();
// Is it the last?
if (comArrEl != last)
{
// let's work it out
// update the token
token = obj.SelectToken(comArrEl);
}
// Yes its the last
else
{
var rawData = (string) obj.SelectToken(component.QueryComponent);
if (rawData != null)
{
// https://stackoverflow.com/questions/23131414/culture-invariant-decimal-tryparse
var style = NumberStyles.Any;
if (ExponentHelper.IsExponentialFormat(rawData))
{
style = NumberStyles.Float;
}
// If it is an exponent
if (decimal.TryParse(rawData, style, CultureInfo.InvariantCulture,
out decimal val))
{
if (val > 0)
{
// Update it
_currencyPairComponentService.UpdatePairValue(component.Id, val);
}
}
}
}
}
}
else
{
// Something bad happened
return false;
}
}
}
return false;
}
What type of data input works?
well, the 'token' property can be this
{
"?xml": {
"@version": "1.0",
"@encoding": "UTF-8"
},
"gesmes:Envelope": {
"@xmlns:gesmes": "http://www.gesmes.org/xml/2002-08-01",
"@xmlns": "http://www.ecb.int/vocabulary/2002-08-01/eurofxref",
"gesmes:subject": "Reference rates",
"gesmes:Sender": {
"gesmes:name": "European Central Bank"
},
"Cube": {
"Cube": {
"@time": "2018-12-06",
"Cube": [
{
"@currency": "USD",
"@rate": "1.1351"
},
{
"@currency": "JPY",
"@rate": "128.04"
},
{
"@currency": "BGN",
"@rate": "1.9558"
},
{
"@currency": "CZK",
"@rate": "25.890"
},
{
"@currency": "DKK",
"@rate": "7.4635"
},
{
"@currency": "GBP",
"@rate": "0.88930"
},
{
"@currency": "HUF",
"@rate": "323.75"
},
{
"@currency": "PLN",
"@rate": "4.2881"
},
{
"@currency": "RON",
"@rate": "4.6548"
},
{
"@currency": "SEK",
"@rate": "10.2355"
},
{
"@currency": "CHF",
"@rate": "1.1304"
},
{
"@currency": "ISK",
"@rate": "138.40"
},
{
"@currency": "NOK",
"@rate": "9.7028"
},
{
"@currency": "HRK",
"@rate": "7.3965"
},
{
"@currency": "RUB",
"@rate": "75.9421"
},
{
"@currency": "TRY",
"@rate": "6.0947"
},
{
"@currency": "AUD",
"@rate": "1.5745"
},
{
"@currency": "BRL",
"@rate": "4.4370"
},
{
"@currency": "CAD",
"@rate": "1.5229"
},
{
"@currency": "CNY",
"@rate": "7.8239"
},
{
"@currency": "HKD",
"@rate": "8.8669"
},
{
"@currency": "IDR",
"@rate": "16481.65"
},
{
"@currency": "ILS",
"@rate": "4.2367"
},
{
"@currency": "INR",
"@rate": "80.4950"
},
{
"@currency": "KRW",
"@rate": "1273.03"
},
{
"@currency": "MXN",
"@rate": "23.3643"
},
{
"@currency": "MYR",
"@rate": "4.7271"
},
{
"@currency": "NZD",
"@rate": "1.6517"
},
{
"@currency": "PHP",
"@rate": "60.012"
},
{
"@currency": "SGD",
"@rate": "1.5560"
},
{
"@currency": "THB",
"@rate": "37.282"
},
{
"@currency": "ZAR",
"@rate": "15.9797"
}
]
}
}
}
}
Update method: need a sample input for line 6?
This will obtain the @rate property.
gesmes:Envelope/Cube/Cube/Cube/0=>@rate
Easier sample:
[
97.935,
1745.41815607,
97.936,
315.65150703,
-6.58,
-0.063,
97.93,
664801.20661302,
105.67,
96.23
]
Line 6 would be just "0"
来源:https://stackoverflow.com/questions/53649570/how-do-you-check-if-a-json-array-is-an-array-of-objects-or-primitives-recursivel