Easiest way to retrieve values from D365 dataverse JSON? - c#

I've recently started working with JSON structures from Microsoft Dynamics 365 dataverse. A lot of their odata is structured like this:
{
"#odata.context":"https://....",
"value":[
{
"#odata.etag":"W/\"Jz....",
"dataAreaId":"foo",
"ItemNumber":"TEST",
"IsPhantom":"No"
}
]
}
I simply want to get the value of ItemNumber, which in this case is TEST. This seems like it should be very simple, but after an hour and a dozen different approaches, I'm wondering what I'm missing.
When using Newtsonsoft, it seems like all their approaches require a fully-baked class that perfectly matches the json structure, and the correct specification of complex combinations of <T>. This is tedious because I work with many different json data sets and almost all are different from each other in terms of the attributes and their types. I tried things like:
dynamic try1 = JsonConvert.DeserializeObject<dynamic>(jsonResult);
dynamic try2 = JObject.Parse(jsonResult);
dynamic try3 = JsonConvert.DeserializeObject<ExpandoObject>(jsonResult, new ExpandoObjectConverter());
JObject try4 = (JObject)JsonConvert.DeserializeObject<dynamic>(jsonResult);
Then I gave up on Newtonsoft and tried JavaScriptSerializer, but ran into similar dead ends.
var try5 = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(jsonResult);
string try6 = Utilities.SafeTrim(jsonObj["value"]);
In every case I end up with a valid object, but I can never figure out how to traverse down into the object and grab the value I want. I frequently end up with completely useless constructs like this from the VS watch window, which it delivers just to mock me:
new System.Linq.SystemCore_EnumerableDebugView<System.Collections.Generic.KeyValuePair<string, Newtonsoft.Json.Linq.JToken>>(new System.Linq.SystemCore_EnumerableDebugView<Newtonsoft.Json.Linq.JToken>((new System.Linq.SystemCore_EnumerableDebugView<System.Collections.Generic.KeyValuePair<string, Newtonsoft.Json.Linq.JToken>>(try1).Items[1]).Value).Items[0]).Items[2]
I'm sure someone has the correct and elegant way to solve this, which would be fine. But what I would really love is a way to take any JSON string and convert to an object, array, or collection that I can easily tease out any value with nested array syntax (or something equally simple). So for my example, maybe something like this:
jsonObject["value"]["ItemNumber"]
Is something like that so difficult?

This code:
const string jsonInput = #"{
'odata.context':'https://....',
'value':[{
'#odata.etag':'W/z....',
'dataAreaId':'foo',
'ItemNumber':'TEST',
'IsPhantom':'No'
}]}";
dynamic try1 = JsonConvert.DeserializeObject(jsonInput)!;
Console.WriteLine(try1["value"][0]["ItemNumber"]);
returns
TEST
as expected

Related

Get a specific part of my JSON data in C#

I have this JSON:
{
"response":
{
"data":
[
{
"start":1,
"subjects":["A"]
},
{
"start":3,
"subjects":["B"]
},
{
"start":2,
"subjects":["C"]
}
]
}
}
And I want to get only the "subject" data from the object with it's "start" value to be the smallest one that is greater than 1.3, which in this case would be C. Would anybody happen to know how such a thing can be achieved using C#?
I want to extend a bit on the other answers and shed more light into the subject.
A JSON -- JavaScript Object Notation - is just a way to move data "on a wire". Inside .NET, you shouldn't really consider your object to be a JSON, although you may colloquially refer to a data structure as such.
Having said that, what is a JSON "inside" .NET? It's your call. You can, for instance treat it as a string, but you will have a hard time doing this operation of finding a specific node based on certain parameters/rules.
Since a JSON is a tree-like structure, you could build your on data structure or use the many available on the web. This is great if you are learning the workings of the language and programming in general, bad if you are doing this professionally because you will probably be reinventing the wheel. And parsing the JSON is not a easy thing to do (again, good exercise).
So, the most time-effective way of doing? You have two options:
Use a dynamic object to represent your JSON data. A dynamic is a "extension" to .NET (actually, an extension to the CLR, that is called DLR) which lets you create objects that doesn't have classes (they can be considered to be "untyped", or, better, to use duck typing).
Use a typed structure that you defined to hold your data. This is the canonical, object-oriented, .NET way of doing it, but there's a trade-off in declaring classes and typing everything, which is costly in terms of time. The payoff is that you get better intellisense, performance (DLR objects are slower than traditional objects) and more safe code.
To go with the first approach, you can refer to #YouneS answer. You need to add a dependency to your project, Newtonsoft.Json (a nuget), and call deserialize to convert the JSON string to a dynamic object. As you can see from his answer, you can access properties in this object as you would access then on a JavaScript language. But you'll also realize that you have no intellisense and things such as myObj.unexistentField = "asd" will be allowed. That is the nature of dynamic typed objects.
The second approach is to declare all types. Again, this is time consuming and on many cases you'll prefer not to do it. Refer to Microsoft Docs to get more insight.
You should first create your data contracts, as below (forgive me for any typos, I'm not compiling the code).
[DataContract]
class DataItem
{
[DataMember]
public string Start { get; set; }
[DataMember]
public string[] Subjects { get; set; }
}
[DataContract]
class ResponseItem
{
[DataMember]
public DateItem[] Data { get; set; }
}
[DataContract]
class ResponseContract
{
[DataMember]
public ResponseItem Response { get; set; }
}
Once you have all those data structures declared, deserialize your json to it:
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
var deserializer = new DataContractJsonSerializer(typeof(ResponseContract));
return (T)deserializer.ReadObject(ms);
}
The code above may seem a bit complicated, but follow a bit of .NET / BCL standards. The DataContractJsonSerializer work only with streams, so you need to open a stream that contains your string. So you create a memory stream with all the bytes from the json string.
You can also use Newtonsoft to do that, which is much simpler but, of course, still requires that extra dependency:
DataContract contract = JsonConvert.DeserializeObject<DataContract>(output);
If you use this approach you don't need the annotations (all those DataMember and DataContract) on your classes, making code a bit more clean. I very much prefer using this approach than DataContractJsonSerializer, but it's your call.
I've talked a lot about serializing and deserializing objects, but your question was, "How do I find a certain node?". All the discussion above was just a prerequisite.
There are, again and as usual, a few ways of achieving what you want:
#YouneS answer. It's very straightforward and achieves what you are looking for.
Use the second approach above, and then use your typed object to get what you want. For instance:
var contract = JsonConvert.DeserializeObject<DataContract>(output);
var query = from dataItem in contract.Response.Data
where dataItem.Start > 1.3
order by dataItem.Start;
var item = query.FirstOrNull();
Which will return the first item which, since it's ordered, should be the smallest. Remember to test the result for null.
You can use a feature from Newtonsoft that enables to directly find the node you want. Refer to the documentation. A warning, it's a bit advanced and probably overkill for simple cases.
You can make it work with something like the following code :
// Dynamic object that will hold your Deserialized json string
dynamic myObj = JsonConvert.DeserializeObject<dynamic>(YOUR-JSON-STRING);
// Will hold the value you are looking for
string[] mySubjectValue = "";
// Looking for your subject value
foreach(var o in myObj.response.data) {
if(o.start > 1.3)
mySubjectValue = o.subjects;
}

Dummy Objects Good or Bad

I am working on a project that communicates a lot of data with a server. This data is in a json format. We end up creating a lot of dummy objects to parse the json data. This leads to having a lot of classes that just contain class members. Is there a better way of doing things?
thanks
Assuming that you are using NewtonSoft's JSON parser or something similar, you have a couple of choices here. The usual use case here is to deserialize to a named type, thus:
var parsedMessage = JsonConvert.DeserializeObject<Message>(content.AsString());
If you have many types for each differnet JSON message type you wish to receive and wish to avoid to, you can do the following:
var parsedMessage = JsonConvert.DeserializeObject<dynamic>(content.AsString());
This will give you a dynamic object that you can inspect and should also work given other Json libraries. Alternatively, NetwtonSoft also provides the following method:
public static T DeserializeAnonymousType<T>(string value, T anonymousTypeObject);
This will allow you to deserialize to an anonymously typed object rather than a dynamic object.

C# JSON.NET How can I create generic telegram?

I am building a C# (.NET 4.5) generic "telegram" - for lack of a better word - that communicates between a plc and a regular pc using tcp and JSON, especially JSON.NET. I have a tcp server set up and working, and I can create Json strings out of objects, which can then be passed on. So I am left with defining a standard way to pass information, commands and events back and forth. What I am now struggling with is how to easily format the "payload". I have a JsonTelegram class with a dynamic variable called "payload" - more or less this:
private dynamic _payload;
public dynamic payload
{
get
{
return _payload;
}
set
{
_payload = value;
updateContents();
}
}
So if I update this variable like this:
myTelegram.payload = rampTime;
I would like to see the final JSON be this:
{
"purpose": "Update this variable",
"payload": {
"rampTime": 2000
},
"timeStamp": "2000-01-01T00:00:00.000-00:00",
"returnID": "2000-01-01T00:00:00.000-00:00"
}
It is the payload part that I am really struggling with, because that code there will end up looking like this:
{
"purpose": "Update this variable",
"payload": 2000,
"timeStamp": "2000-01-01T00:00:00.000-00:00",
"returnID": "2000-01-01T00:00:00.000-00:00"
}
when updateContents() uses this:
... Newtonsoft.Json.JsonConvert.SerializeObject(_payload) ...
Do you see how the word "ramp" gets replaced with "payload"? I know I could just make a method and pass both the object and the name of the object - this would definitely work. But I would like to get trickier than that, just because it should be possible, and also because down the road I can see some other reasons that this design may work better. I want the code to simply know the name of the variable that called it and use that. I have seen many suggestions using some stuff I barely understand, but when I try to adapt it, it seems to get hung up on one issue or another. So I thought I would ask a fresh question, because surely many people want to do the same thing with JSON and the new dynamics?
StackOverflow has information on attributes and dictionaries, reflection, something to do with t, expressions, and so on. For instance, this question: How do you get a variable's name as it was physically typed in its declaration? and this Finding the variable name passed to a function
I don't mind making the class code as messy and hacky as all get out, but I want the calling code to be as clean as "myTelegram.payload = rampTime;"
I'm sorry that I can't wade through all the suggestions well enough - I spend most of my time writing plc code, and just now have a plc that one side does C#.
Also, if there is simply a more elegant way to do things that is totally different, I am eager to hear it.
Thanks!
It sounds like you're looking for an ExpandoObject. With an ExpandoObject, your telegram looks something like:
class Telegram
{
private dynamic _payload = new ExpandoObject();
public dynamic payload
{
get
{
return _payload;
}
}
public string ToJson()
{
...
}
}
And in your code:
var telegram = new Telegram();
telegram.payload.rampTime = 2000;
Console.WriteLine(telegram.ToJson());

Serialize an array into JSON objects using .NET

The serialization of the array returns the following JSON:
[{"Code":"AAAA","Description":"Description of AAAA"},{"Code":"BBBB","Description":"Description of BBBB"}]
My goal is to return the following JSON:
{"AAAA":{"Description":"Description of AAAA"},"BBBB":{"Description":"Description of BBBB"}}
You can achieve something simliar (not exactly the same you are expecting) if instead of serializing an array, build a temporary Dictionary and serialize it.
var dict = new Dictionary<String, YourClass>();
foreach (YourClass yourObj in listOfObjs)
{
dict[yourObj.Code] = yourObj;
}
// then serialize "dict"
You could add a rule to your JSON serializer to make it avoid serializing "code" property in YourClass, and you will end up with a JSON object exactly as you show in your example.
You'll need to either use a class that has the "AAAA" and "BBBB" properties, or you'll need to serialize a dictionary instead. As it is, you're serializing an array and getting an array.
The table on this blog post shows several search starting points.
.Net has built-in System.Web.Script.Serialization.JavaScriptSerializer, here, with examples
The .Net one doesn't specially serialize Dictionary the way you want, but some of the others do, I believe. However, I think there are reasons NOT to want a general serialization routine the way you've requested - though I can't list them certainly.

Create an anonymous type object from an arbitrary text file

I need a sensible way to draw arbitrary text files into a C# program, and produce an arbitrary anonymous type object, or perhaps a composite dictionary of some sort.
I have a representative text file that looks like this:
adapter 1: LPe11002
Factory IEEE: 10000000 C97A83FC
Non-Volatile WWPN: 10000000 C93D6A8A , WWNN: 20000000 C93D6A8A
adapter 2: LPe11002
Factory IEEE: 10000000 C97A83FD
Non-Volatile WWPN: 10000000 C93D6A8B , WWNN: 20000000 C93D6A8B
Is there a way to get this information into an anonymous type object or some similar structure?
The final anonymous type might look something like this, if it were composed in C# by hand:
new
{
adapter1 = new
{
FactoryIEEE = "10000000 C97A83FC",
Non-VolatileWWPN = "10000000 C93D6A8A",
WWNN = "20000000 C93D6A8A"
}
adapter2 = new
{
FactoryIEEE = "10000000 C97A83FD",
Non-VolatileWWPN = "10000000 C93D6A8B",
WWNN = "20000000 C93D6A8B"
}
}
Note that, as the text file's content is arbitrary (i.e. the keys could be anything), a specialized solution (e.g. that looks for names like "FactoryIEEE") won't work. However, the structure of the file will always be the same (i.e. indentation for groups, colons and commas as delimiters, etc).
Or maybe I'm going about this the wrong way, and you have a better idea?
You're going about this the wrong way. Your "anonymous type object" data is hard to construct and hard to use. To construct it, you'd have to use reflection trickery. And for what?
Consider a PrintReport function. This function would not get any simpler because of the ATO usage. Far from it, it'd get more complicated and slow, having to use reflection itself to iterate over the keys. Your solution might have made sense if there was a small, fixed number of possible keys. Then usage syntax such as "obj.FactoryIEEE" might have been preferred.
The way I'd go about this is with a List<Dictionary<string, string>>, or, say a List<AdapterRecord> where AdapterRecord is
class AdapterRecord
{
public string Name { get; set; }
public Dictionary<string, string> Parameters { get; set; }
}
Check out this 2-part article. It parses XML files in a fluent way. It can be adapted to parse your text files I think.
Fluent XML Parsing Using C#'s Dynamic Type Part 1
Fluent XML Parsing Using C#'s Dynamic Type Part 2
It uses C# 4 dynamic typing to do what you want.
It looks like there's no way to do this even in C# 4.
Besides, thinking about it if you don't know what the keys will be then any sort of strongly typed access would be tough, and a simple split + insertion into dictionary/list/etc. would make more sense.
While I don't love them, perhaps a DataTable would serve...

Categories