Parsing multiple JSON objects sent via Express client-side in C# - c#

I'm trying to Parse two objects sent via Express to my unity client into c# objects.
Here I am sending the search results of a mongoose.model.find
res.send({account: accountData, score: scoreData})
Over in my Unity client this is the function that grabs the data above from my node.js server. I recieve the information here as the www.downloadHandler.text
using (UnityWebRequest www = UnityWebRequest.Post(hostURL + "Login", loginForm))
{
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log("Error Sending Login Detail: " + www.error);
}
else
{
if (www.responseCode == 200)
{
Debug.Log(www.downloadHandler.text);
thisUser = username;
menu.LoginPassed();
leaderboard.LoadPlayerScoreData(www.downloadHandler.text);
}
else
{
Debug.Log("Login Unsuccessful. Reason: " + www.downloadHandler.text);
}
}
}
}
for reference here is the output of the Debug.Log(www.downloadHandler.text) so we can see what we are working with:
{
"account":[
{
"_id":"62fc40709bc9c241c7f650cd",
"username":"BaggyJ",
"password":"12345",
"email":"#hotmail",
"hhs":0,
"level":1,
"xp":0,
"currency":0,
"premiumCurrency":0,
"__v":0
}
],
"score":[
{
"_id":"62fb4efe6e3a942138405b3e",
"username":"BaggyJ",
"score":420,
"__v":0
}, // ...
]
}
Here I am recieving the information as intended, the account data contains the player account informaton and the score data is an array of the players best scores. So far so good!
The next step obviously, is to parse this information into a usable C# object. I attempted to do this in the same way I had previously accomplished in this project.
I will show you here the solution for this I'm using that does not work, and an example of the solution when it does work.
Firstly, what I have thats not working:
I created two C# serialized classes to represent the information.
[System.Serializable]
public class DataPackage
{
public string account;
public string score;
}
And
[System.Serializable]
public class DataPackages
{
public DataPackage[] dataPackages;
}
Finally, I use the JSON Utility function to parse the data string included above
public DataPackages ParseData(string jsonText)
{
DataPackages dataPackages = JsonUtility.FromJson<DataPackages>("{\"dataPackages\":" + jsonText + "}");
return dataPackages;
}
If I attempt to use the above code the dataPackages.dataPackages object is always null.
The function is meant to result in an object with two strings, the first to represent the account data and the second to represent the score data.
These two string will then be parsed a second time into their final C# objects for use.
It is worth noting here that the parsing tools for account data and score data are built the same way and function as expected. I will include one below for reference:
[System.Serializable]
public class ScorePacket
{
public string username;
public string score;
}
[System.Serializable]
public class ScorePackets
{
public ScorePacket[] scorePackets;
}
public ScorePackets ParseScore(string jsonText)
{
ScorePackets scorePackets = JsonUtility.FromJson<ScorePackets>("{\"scorePackets\":" + jsonText + "}");
return scorePackets;
}
The above code will return an c# object where I can access its data as such: scorePackets.scorePackets[0].score
Any insight into why I cannot accomplish this with DataPackages would be greatly appreciated!
EDIT
So I've gotten it a little closer but not quite.
I have replaced:
DataPackages dataPackages = JsonUtility.FromJson<DataPackages>("{\"dataPackages\":" + jsonText + "}");
With:
DataPackages dataPackages = JsonUtility.FromJson<DataPackages>("{\"dataPackages\":[" + jsonText + "]}");
This successfully creates a C# object with the correct fields HOWEVER both of these field are empty.
Debug.Log(dataPackages.dataPackages[0].score)
Debug.Log(dataPackages.dataPackages[0].account)
Both return empty strings.
Edit#2
We are getting so close I can taste it.
So in addition to the above changes I have also made the following change to my node.js server:
res.send({account: accountData.toString(), score: scoreData.toString()})
Now if I:
Debug.Log(dataPackages.dataPackages[0].score.ToString());
Debug.Log(dataPackages.dataPackages[0].account.ToString());
My output is:
[object Object],[object Object],[object Object],[object Object],[object Object]
[object Object]
.ToString() failed to get the json text for these objects

I figured it out!
I needed to change the way I declared my DataPacket class from containing strings to containing object arrays.
[System.Serializable]
public class DataPackage
{
public AccountPacket[] account;
public ScorePacket[] score;
}
Solved my problems

Related

gRPC: How to use dynamically typed values?

I'm trying using gRPC dynamically typed values but with the little information about their usefulness, It's almost impossible to do this... So I will show the image/code that I have problems and the questions that are eating my brain
gRPC Method I'm doing:
public override Task<HelloReply2> TestObject(Status request, ServerCallContext context) {
//The part I may have problems
var status = new Status();
//here I want a User that corresponds to my request.Data
//example -> request.Data = User1 (gives me null if User1 don`t exist in db)
// request.Data = 14 (gives me null if 14 don`t exist in db)
// request.Data = true (gives me null if true don`t exist in db)
var a1 = _context.Users_5.FirstOrDefault(x => x.Username.Equals(request.Data));
var b1 = _context.Users_5.FirstOrDefault(x => x.Email.Equals(request.Data));
var c1 = _context.Users_5.FirstOrDefault(x => x.Age.Equals(request.Data));
var d1 = _context.Users_5.FirstOrDefault(x => x.Test.Equals(request.Data));
//is a bool
//here i want too Create dynamic values
status.Data = Value.ForStruct(new Struct {
Fields =
{
["Integer"] = Value.ForNumber(c1!.Age),
["StringName"] = Value.ForString(a1!.Username),
["StringEmail"] = Value.ForString(b1!.Email),
["Boolean"] = Value.ForBool(d1!.Test)
}
});
//Below is just a simple string who gives different string (depending on the
//data Status (also how to read the message from the status.Data ?)
HelloReply2 hello = new();
if(a1 != null)
{
hello.Message = "There is a User with the Username " + request.Data + ". His Email is " + a1.Email;
} else if (b1 != null) {
hello.Message = "There is a User with the Email " + request.Data + ". His Username is " + b1.Username;
}
else if (c1 != null)
{
hello.Message = "There is at least one User with that Age of " + request.Data + ". His Username is " + c1.Username;
}
else if (d1 != null)
{
if(d1.Test == true)
{
hello.Message = "There is at least one User who dislikes chocolate: " + request.Data + ". His Username is " + d1.Username;
} else
{
hello.Message = hello.Message = "There is at least one User who likes chocolate: " + request.Data + ". His Username is " + d1.Username;
}
}
else
{
hello.Message = "We didn't find something with the value that the User put in. Value:" + request.Data;
}
return Task.FromResult(hello);
}
Questions: How to Get the one Value from my gRPC? How to convert a "Object" in c# (one string, one integer or one List) into a ONE value of google.protobuf.Value (so it not give me errors like this Controller from a Web Api below)? Is something wrong with my gRPC Service Method (is something wrong reading the dynamic values? Can I do that calls for getting a User for a DB? How to read dynamic values?)
// I try using Google.Protobuf.WellKnownTypes.Value obj but
//not workings because gives me a lot of values to put
[HttpGet("TypeObject/{obj}")]
public async Task<ActionResult<HelloReply2>> TypeObject([FromRoute] Object obj){
Status objRequest = new Status { Data = (Google.Protobuf.WellKnownTypes.Value)
obj };
//cannot do this (gives me error of casting but the ideia is doing something
//similar to this)
var hello = await _greetClient.TestObjectAsync(objRequest);
return Ok(hello);
}
Any help on how to resolve this error of using Value gRPC or if is something wrong with the code is always welcome.
Edit:
One day after this question I don't have any solutions / progress. I was think of doing Any or OneOf for testing but it also gives me errors (who don't make sense at all). This code from Microsoft (C# Format part is not recognize) doesn't work in my project with the protos reload (the problem is not in the protos)
Link: https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/protobuf-any-oneof
How I can use Any / OneOf without give me error in the Formating? What is the difference between Value and this two? Can the three (Value, Any, OneOf) be dynamic/Object values (if yes how to convert the types)?
Edit 2:
Still have problems, I'm trying using gRPC Any , and maybe have some progress (not all).
So with Any I have my method in the server gRPC and it is like this
public override Task<HelloReply2> TestObject3(AnyMessage request, ServerCallContext context){
HelloReply2 anyMessageResponse;
var y = request.TypeUrl;
switch (request.TypeUrl)
{
case "type.googleapis.com/any.HelloRequest":
var string_1 = request.Unpack<HelloRequest>();
anyMessageResponse = new HelloReply2{
Message = "You type String: " + $"{string_1.Name}"
};
break;
case "type.googleapis.com/any.TestInteger1":
var integer_1 = request.Unpack<TestInteger1>();
anyMessageResponse = new HelloReply2{
Message = "You type Integer: " + $"{integer_1.Message}"
};
break;
case "type.googleapis.com/any.TestBool1":
var bool_1 = request.Unpack<TestInteger1>();
anyMessageResponse = new HelloReply2{
Message = "You type Bool: " + $"{bool_1.Message}"
};
break;
default:
throw new InvalidOperationException("Unexpected type URL.");}
return Task.FromResult(anyMessageResponse);
}
This ideia comes from here (https://github.com/grpc/grpc-dotnet/issues/917), but the client part their don't have any much info or I don't understand that part
This is what I did in the WebApi (who is my client and the code is similar to the above one)
using AnyMessage = Google.Protobuf.WellKnownTypes.Any;
[HttpGet("TypeObject3/{obj3}")]
public async Task<ActionResult<HelloReply2>> TypeObject3([FromRoute] string obj3)
{
AnyMessage objRequest = new() { TypeUrl = obj3 };
var hello = await _greetClient.TestObject3Async(objRequest);
var l = hello.Message;
return Ok(hello);
}
First I had the variable Any declared in the method instead of string but as you can only put string and stringBytes so I preferred to put it like this (with the string as an obj3 variable) but my goal is to see if the variable is of type TestBool1 or TestInteger1 as I have declared in the protos and not be a string that I will be able to see, and the biggest problem was if I had more variables inside the messages how to proceed? So my secondary question is how to use Any on the client side via the Web-Api? I forgot to say but I'm using .Net 6 Core and for testing I'm using Swagger, where at this moment my error hits the Exception dictated by the Server method.
Questions: Why TypeUrl is a string and not object? How to fix my problem? How to test the object type (or string) for more values if the messages was with 1 more types?
Also I will show my test proto too show how I'm doing this
import "google/protobuf/struct.proto";
import "google/protobuf/any.proto";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayNormalHello (Empty_2) returns (HelloReply);
rpc SayHello (HelloRequest) returns (HelloReply2);
rpc TestInt (TestInteger1) returns (HelloReply2);
rpc TestBoolean (TestBool1) returns (HelloReply2);
rpc TestObject (Status) returns (HelloReply2); //Not working
rpc TestObject2 (Status2) returns (HelloReply2); //Not working
rpc TestObject3 (google.protobuf.Any) returns (HelloReply2); //Also
//Not working
}
message Empty_2{
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
// The response message containing the greetings.
message HelloReply2 {
string message = 1;
}
message TestInteger1 {
int32 message = 1;
}
message TestBool1 {
bool message = 1;
}
message Status {
google.protobuf.Value data = 1;
}
message Status2 {
google.protobuf.Any data = 1;
}
Any help is welcome.
In the project I developed, I needed an infrastructure where I could perform dynamic operations such as REST service using gRPC. As I understand it, what you want is something similar to this.
I have developed a solution for this. It might work for you too.
You can gain some flexibility by making a definition with a single String field for Requests and Responses that you want to be Dynamic, and then using this field to hold JSON data as strings.
For example, instead of defining different responses for different types, you can solve it by making one definition like this.
message HelloReply {
// Stringified JSON Data
string data = 1;
}

How do I pass the json object to an instance of the PowerShell as a parameter to the script in C#?

I am building an application that interacts with Active Directory using System.Management.Automation (Not using Directory Services because currently new to that library and learning it). To update the group membership of for a group in the active directory I am creating a JSON object on my view and invoking a function to pass the object & the URI from front end to back end via a function in my controller.
The basic idea is to allow removal of AD group members in bulk by passing the JSON object as a parameter to the shell script which will be executed in an instance of PowerShell created in the function. I am using .ajax call to invoke the controller function and passing the JSON object that I generated as an argument along with the current URI. The shell.commands.AddParameter() function accepts argument in only string format. So, I typecasted it with ToString() and converting it to JSON in the PowerShell script. I am passing the URL from code behind as the URL is subject to change. I am not getting any errors However, I am also not able to see any update in membership in the AD. Json Object is getting generated from HTML Table.
My shell script
param($objMemberUpdate, $uri)
$body = $objMemberUpdate | ConvertTo-JSON
Invoke-WebRequest -Uri $uri -Method Post -Body $objMemberUpdate
My Controller Function to Invoke PowerShell Instance and executing Shell Script file from specified location.
private string UpdateMemberList(JsonResult objMemberUpdate)
{
var uri = HttpContext.Request.Url.AbsoluteUri;
var shell = PowerShell.Create();
shell.Commands.AddCommand(AppDomain.CurrentDomain.BaseDirectory + "Shell\\Set-ADGroupMembership.ps1").AddParameter(objMemberUpdate.ToString(), uri);
var results = shell.Invoke();
shell.Dispose();
return results.ToString();
}
The Ajax Call that I am calling on a button click on my HTML page.
//Make Array Object to pass in the API For Membership Update
$("#btnUpdate").click(function () {
var RemoveMembers = [];
var RemoveAfter = [];
var MemberUpdate = {};
var GroupGUID = "";
$("table [id*=ddlReqdAdjustment]").each(function () {
if ($(this).val() != "Keep") {
GroupGUID = $(this).parent().parent().children().eq(4)[0].innerText;
var date = $(this).parent().parent().children().eq(8)[0].firstElementChild.value;
var ObjectGUID = $(this).parent().parent().children().eq(3)[0].innerText + "##" + $('#ddlDirectory').val();
if ($(this).val() == "Remove") {
var format = ObjectGUID;
RemoveMembers.push(format);
} else {
var format = date + "|" + ObjectGUID;
RemoveAfter.push(format);
}
}
});
MemberUpdate = {
"Directory": $('#ddlDirectory').val(),
"Group": GroupGUID,
"Remove": RemoveMembers,
"RemoveAfter": RemoveAfter,
"ResultFormat": "json",
"OnBehalfOf": "11112201"
};
console.log(MemberUpdate);
$.ajax({
type: "POST",
url: "/Group/UpdateMemberList",
data: { objMemberUpdate: MemberUpdate },
success: function (response) {
alert(response.message);
}
});
The selected member in the table is supposed to get removed from the Group whose GroupGUID (ObjectGUID attribute in AD) is mentioned from the AD. However, no compile time or no runtime error is encountered and nit even any changes are reflected and I think this must be due to problem with my JSON Object?
The first problem is here:
private string UpdateMemberList(JsonResult objMemberUpdate)
The JsonResult class is designed to be used in a response (returned from a controller action), not as a parameter.
Instead, create your own class with the properties you need, and use that. Something like this:
public class MemberUpdate {
public string Directory { get; set; }
public Guid Group { get; set; }
public List<string> Remove { get; set; }
public List<string> RemoveAfter { get; set; }
public string ResultFormat { get; set; }
public string OnBehalfOf { get; set; }
}
And your action definition will then be like this:
public string UpdateMemberList(MemberUpdate objMemberUpdate)
ASP.NET will deserialize the JSON sent from the browser into an instance of your class automatically.
That said, I want to address something you said:
using System.Management.Automation (Not using Directory Services because currently new to that library and learning it)
Regardless of how you implement this, you will have to learn something. You may as well learn the better way :)
Calling PowerShell from C# is awful. I've done it several times, and it always gives me a headache. It means more prerequisites (you need the AD module installed), more things to break, and just slower performance (since you're communicating with a separate powershell.exe process).
There are lots of examples online about how to add and remove members from groups in C#, like here - both the code in the question itself (using System.DirectoryServices), and the code in the accepted answer (using System.DirectoryServices.AccountManagement) will work. But using System.DirectoryServices directly, while a little more complicated, always performs faster than System.DirectoryServices.AccountManagement. (I talked about why in an article I wrote).

Newtonsoft read array in C#

I'm trying to get an entry of a json array into a messagebox but i can't seem to get the entry it always gives me
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: C. Path '', line 0, position 0.'
No matter what i do here is the json i use:
[
{
"code": 200,
"token": "yourtokenhere",
"id": "youridhere",
"user": "user",
"message": "Successfully connected"
}
]
Here is what i got so far:
string json = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\zero.json";
login fetch = new login();
Newtonsoft.Json.JsonConvert.PopulateObject(json, fetch);
MessageBox.Show(fetch.user);
And here is the C# class i made:
class login
{
public string code;
public string token;
public string id;
public string user;
public string message;
}
Thanks in advance!
EDIT: The full code is available here
Your problem is two-fold.
One issue is that you want to deserialize an array into an object. Other answers here already have addressed this issue.
The other issue, the one responsible for the error in your question, is that you used the Newtonsoft.Json.JsonConvert.PopulateObject method incorrectly. Look at its documentation:
public static void PopulateObject(
string value,
Object target
)
Parameters
value
Type: SystemString
The JSON to populate values from.
target
Type: SystemObject
The target object to populate values onto.
Note that this method expects the Json string data as first argument, not a file path!
So what happens in your code is the variable json gets assigned a file path to your Json file (string json = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\zero.json";). The string variable json will now have a content like #"C:\Users\UserName\AppData\Roaming\zero.json".
Your code passes this string as json data to the PopulateObject() method. But since this string is not json data (it's just the file path), PopulateObject() fails. (Note that the error message complains about the json data starting unexpectedly with a C.)
One possibility of implementing what you want to do is to first read the Json file content into a string, and then pass this to the Json deserializer (in the same way as demonstrated by the other answers):
string filePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\zero.json";
string json = File.ReadAllText(filePath);
List<login> fetch = Newtonsoft.Json.JsonConvert.DeserializeObject<List<login>>(json);
JSON data is an array and you are trying to deserialize it into an object.
Try below code:
List<login> fetch = Newtonsoft.Json.JsonConvert.DeserializeObject<List<login>>(json);
Please check
JsonConvert.DeserializeObject

Unity-Firebase Realtime Database - OrderByChild or OrderByValue does not work

I try to develop a leaderboard system using Firebase Realtime Database using Unity.
I can store and load the score values, everything works ok.
However, when I try to retrieve the scores in order, I cannot manage to do. I do as exactly described in:
https://firebase.google.com/docs/database/unity/retrieve-data#sort_data
The structure of the data in my firebase console is as follows:
My "orderByChild" using code is as below:
// Set the leaderboard reference
m_leaderBoardDBReference = mDatabaseReference.Child ("Users");
// Get the data, order by child "highScore"
m_leaderBoardDBReference.OrderByChild("highScore").GetValueAsync().ContinueWith(task => {
if (task.IsFaulted) {
// Handle the error...
Debug.Log("Data " + lbAddress + " not found");
}else if (task.IsCompleted) {
DataSnapshot snapshot = task.Result;
// Print the raw json value of the leaderboard data
Debug.Log("Leaderboard Raw Json Value\n");
Debug.Log(snapshot.GetRawJsonValue());
}
});
I get the following result:
Leaderboard Raw Json Value
{"user3":{"highScore":49},"user4":{"highScore":0},"user5":{"highScore":69},"user6":{"highScore":155}}
In other words, it is not properly ordered.
I also tried the "OrderByValue" version as follows:
// Set the leaderboard reference
m_leaderBoardDBReference = mDatabaseReference.Child ("Contests/AprilMay");
// Get the data, order by value
m_leaderBoardDBReference.OrderByValue().GetValueAsync().ContinueWith(task => {
if (task.IsFaulted) {
// Handle the error...
Debug.Log("Data " + lbAddress + " not found");
}else if (task.IsCompleted) {
DataSnapshot snapshot = task.Result;
// Print the raw json value of the leaderboard data
Debug.Log("Leaderboard Raw Json Value\n");
Debug.Log(snapshot.GetRawJsonValue());
}
});
The structure:
The output:
Leaderboard Raw Json Value
{"dummy":0,"user3":240,"user5":69,"user6":155}

Calling Json script and displaying the Images within the script

I'm trying to access a Json script, it is in a domain. The script holds a couple of images, which are hyperlinked. I've tried using WWW to access the script, but the image i received was a picture of a huge red question mark. Clearly I'm going about the wrong way with this. So i'm assuming I'm supposed to decode the json script through unity and then display the image with the ability to see next/previous image by clicking? I'm unfamiliar with Json so how about would i call the script and read the the images it's calling?
This is what my code looks like, the code works since I've tried another non Json domain with just an image- and it works perfectly fine.
void Start ()
{
renderer.material.mainTexture = new Texture2D(4,4, TextureFormat.DXT1, false);
url = "http://hosting.xivmedia.com/JsonHome/JSON/more_games.json";
www = new WWW(url);
StartCoroutine(WaitForSeconds(www));
}
IEnumerator WaitForSeconds(WWW www)
{
yield return www;
www.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.data);
imageLoaded = true;
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
void OnGUI()
{
GUI.DrawTexture(new Rect(20, 80, 100, 100), renderer.material.mainTexture as Texture2D, ScaleMode.StretchToFill);
}
Yes, as you said correctly, you'll need to decode the JSON script first. You'll need a JSON parser to do that. The one I highly recommend is JSON .NET For Unity, but MiniJSON should do just fine.
So, assuming your json is just a list of URLs as so:
[
"http://.....",
"http://....."
]
With JSON.NET you would modify your code as follows:
IEnumerator WaitForSeconds(WWW www)
{
yield return www;
string json = www.text;
List<string> imageUrls = JsonConvert.Deserialize<List<string>>(json);
foreach (string imageUrl in imageUrls)
{
WWW imageWWW = new WWW(imageUrl);
imageWWW.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);
if (imageWWW .error == null)
{
Debug.Log("WWW Ok!: " + imageWWW.data);
imageLoaded = true;
}
else
{
Debug.Log("WWW Error: " + imageWWW.error);
}
}
}
If the JSON is more complicated, you can create a class that the JSON can deserialize to. I.e., if the JSON is a list of objects as so:
[
{
"name": "test",
"url" : "http://...."
},
{
"name": "something",
"url" : "http://..."
}
]
Then you would have a simple class
public class ImageData
{
public string name;
public string url;
}
And deserialize the JSON as so
List<ImageData> imageUrls = JsonConvert.Deserialize<List<ImageData>>(json);
foreach (ImageData data in imageUrls)
{
WWW imageWWW = new WWW(data.url);
imageWWW.LoadImageIntoTexture(renderer.material.mainTexture as Texture2D);

Categories