Reputation: 81
I have this json string and i want to get the 4th line (iValue, sValue)
of every record.
My problem here is the keys vary for every record (based on the data type of the value).
Is there any way to do this on C#?
Here is an example:
{ "data": [
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "CID",
"iValue": "13"
},
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "username",
"sValue": "test_user1"
}] }
Upvotes: 4
Views: 6472
Reputation: 7059
Here is kind of a big implementation, you will have to implement this for each iValue
, fValue
, etc however, it speeds up the implementation and usage. First of, here is the usage:
string rawJson = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var values = JsonConvert.DeserializeObject<TakeData>(rawJson).Data.Select(v => v.PureData);
Now values
contains the list. Here is the usage for accessing each:
foreach (var val in values)
{
if (val is IntData i)
{
int myInt = i.iValue;
// use the rest of the properties
}
else if (val is StrData s)
{
string myStr = s.sValue;
// use the rest of the properties
}
}
And here is the implementation:
class TakeData
{
public List<TakeItAll> Data { get; set; }
}
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
PureData = new IntData { pKey = pKey, Entity = Entity, Attribute = Attribute, iValue = iValue };
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
PureData = new StrData { pKey = pKey, Entity = Entity, Attribute = Attribute, sValue = sValue };
}
}
public IPureData PureData { get; private set; }
}
interface IPureData
{
int pKey { get; set; }
string Entity { get; set; }
string Attribute { get; set; }
}
class IntData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public int iValue { get; set; }
}
class StrData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string sValue { get; set; }
}
Of course you can use some alternatives as well. Such as using an enum in TakeItAll
to keep track of the data type (or a type variable) instead of so many classes. This way However the size of the values
object would be larger.
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
ValType = typeof(string);
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
ValType = typeof(int);
}
}
public Type ValType { get; private set; }
}
Upvotes: 1
Reputation: 1301
My two cents:
KeyValuePair
var json = "your json here";
var root = JsonConvert.DeserializeObject<Root>(json);
foreach (var element in root.Data)
{
//===================================================> Here using object because your value type change. You can change it to string if your value is always wrapped in a string (like "13")
var keyValuePair = element.ToObject<Dictionary<string, object>>();
//here, for each object of the 'data' array, you can check if the desidered property exists
if (keyValuePair.ContainsKey("iValue"))
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.ContainsKey("sValue"))
{
var propertyValue = keyValuePair["sValue"];
}
// Or you can check the property name in the desidered position
if (keyValuePair.Keys.ElementAt(3) == "iValue")
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.Keys.ElementAt(3) == "sValue")
{
var propertyValue = keyValuePair["sValue"];
}
}
Where Root
is
class Root
{
[JsonProperty("data")]
public List<JObject> Data { get; set; }
}
With this solution you can always know which property (iValue
or sValue
) is specified. On the contrary, if you use a model class which has both property names, you wouldn't know which property is specified when the value is null
(unless you use additional properties/classes and a custom JsonConverter
).
As Panagiotis Kanavos reminded me, the JObject
class implements IDictionary<string, JToken>
. So, inside your foreach
you could use:
if (element["iValue"] != null)
{
var propertyValue = element["iValue"].Value<string>();
}
if (element["sValue"] != null)
{
var propertyValue = element["sValue"].Value<string>();
}
// Or you can check the property name in the desidered position
var propName = element.Properties().ElementAt(3).Name;
if (propName == "iValue")
{
var propertyValue = keyValuePair["iValue"].Value<string>();
}
else if (propName == "sValue")
{
var propertyValue = keyValuePair["sValue"].Value<string>();
}
Of course you can optimize this code and check for nulls.
Upvotes: 0
Reputation: 816
You could load the Json in a ExpandoObject
var expConverter = new ExpandoObjectConverter();
dynamic objList = JsonConvert.DeserializeObject<List<ExpandoObject>>(json, expConverter);
JSON array to ExpandoObject via JSON.NET
Then once you have loaded it in as a List<ExpandoObject>
you may itterate over it as a dictionary.
foreach(var obj in objList)
{
//convert the object to a Dictionary and select the 4th element.
var yourresult = (obj as IDictionary<string, object>).ElementAt(3);
}
Upvotes: 0
Reputation: 609
Another option is to deserialize to dynamic
and inspect that:
var json = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var result = JsonConvert.DeserializeAnonymousType<dynamic>(json, null);
if (result.data != null)
{
for (var i = 0; i < result.data.Count; i++)
{
if (result.data[i]["iValue"] != null)
// Parse iValue
if (result.data[i]["sValue"] != null)
// Parse sValue
}
}
Upvotes: 0
Reputation: 6977
It's a good idea to deserialize JSON string to concrete class to avoid string manipulation mistake.
public class Datum
{
public object pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string iValue { get; set; }
public string sValue { get; set; }
}
public class DataCollection
{
public List<Datum> data { get; set; }
}
public void Test()
{
var str = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var list = JsonConvert.DeserializeObject<DataCollection>(str);
var keys = list.data.Select(x => x.pKey).ToList();
}
Upvotes: 0
Reputation: 1995
I would deserialize this into an object supporting both types of properties and then by code try parsing either the integer or the string if the integer fails.
If the Attribute
value gives you a clue as to which one to look for, you could also use that to prevent having to try parsing the integer every time.
I would not rely on the property being the "fourth" property every time, as I'm assuming this would be external data, where you may not be able to control whether these properties come out in the exact same order every time (now and in the future).
Upvotes: 0