How to show data in QML json request - json

So bear with me. How to create a model based on json? What is delegate?
Is below logic is correct?
Model -> delegate -> json request -> json get -> show to list view
In below code I can not see any data on screen. How to show data in QML json request?
thanks
UPDATED WORKING CODE:
import VPlayApps 1.0
import QtQuick 2.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import "qrc:/"
Item {
id: item1
anchors.fill: parent
ListModel {
id: ***modelListIP***
}
ListView {
id: listview
anchors.fill: parent
model: ***modelListIP***
delegate: Text {
text: listdata
}
}
function getData() {
var xmlhttp = new XMLHttpRequest();
var url = "https://api.ipify.org?format=json";
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE && xmlhttp.status == 200) {
myFunction(xmlhttp.responseText);
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
function myFunction(response) {
var objValue = JSON.parse(response);
***modelListIP.append( {"listdata": objValue.ip })***
}
Button {
anchors.bottom: parent.bottom
width: parent.width
text: "Get Data"
onClicked: getData()
}
}
This tested on Qt5.9.2 using QML app project.

Your example is totally wrong.
JSON.parse() returns Object, not array. So you cannot call length() on it. Remember - {} - object, [] - array.
Your request returns something like {"ip":"111.111.111.111"}. Where do you see Name here? So you should append items model.append( {"listdata": arr.ip }), not like you do it now. Don't forget to surround the parameter name with quotes.
listview.model.append shoud be replaced with model.append. Learn what is Occam's razor.
model is not good id for item. Using reserved words is a bad style.
So I advice you to read documentation twice when you facing such problems.

Related

How can I send custom object from my callable Firebase Cloud Function in TypeScript to my Unity app?

I'm trying to use Firebase and its callable Cloud Functions for my Unity project.
With the docs and different posts I found on the web I struggle to understand how returning data works. (I come from Azure Functions in C#)
I use TypeScript, and try to return a custom object CharactersResponse:
export class CharactersResponse //extends CustomResponse
{
Code!: CharactersCode;
CharacterID?: string;
}
export enum CharactersCode
{
Success = 0,
InvalidName = 2000,
CharacterNameAlreadyExists = 2009,
NoCharacterSlotAvailable = 3000,
InvalidCharacterClass = 4000,
EmptyResponse = 9000,
UnknownError = 9999,
}
(Custom Response is a parent class with only an UnknownErrorMessage string property, that I use for adding extra message when needed, but only in Unity. I don't need it in my functions.)
I have the same in my C# Unity Project:
public class CharactersResponse : CustomResponse
{
public CharactersCode Code;
public string CharacterID;
}
public enum CharactersCode
{
Success = 0,
InvalidName = 2000,
CharacterNameAlreadyExists = 2009,
NoCharacterSlotAvailable = 3000,
InvalidCharacterClass = 4000,
EmptyResponse = 9000,
UnknownError = 9999,
}
I'm still learning but I found it useful to do this way for displaying correct messages in Unity (and also regarding localization).
When the Code is 0 (Success), I will usually need to get some data at the same time like in this example CharacterID, or CharacterLevel, CharacterName etc.. CharacterResponse will be used for all functions regarding Characters like "GetAllCharacters", "CreateNewCharacter" etc..
My Function (CreateNewCharacter) looks like this:
import * as functions from "firebase-functions";
import { initializeApp } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { CharactersResponse } from "./CharactersResponse";
import { CharactersCode } from "./CharactersResponse";
import { StringUtils } from "../Utils/StringUtils";
// DATABASE INITIALIZATION
initializeApp();
const db = getFirestore();
// CREATE NEW CHARACTER
export const CreateNewCharacter =
functions.https.onCall((data, context) =>
{
// Checking that the user is authenticated.
if (!context.auth)
{
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
// TEST
data.text = '';
// Authentication / user information is automatically added to the request.
const uid: string = context?.auth?.uid;
const characterName: string = data.text;
// Check if UserID is present
if (StringUtils.isNullOrEmpty(uid))
{
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'Missing UserID in Auth Context.');
}
const response = new CharactersResponse();
if (StringUtils.isNullOrEmpty(characterName))
{
response.Code = CharactersCode.InvalidName;
console.log("character name null or empty return");
return response; // PROBLEM IS HERE *****************
}
console.log("end return");
return "Character created is named : " + characterName + ". UID = " + uid;
});
In Unity, the function call looks like this:
private static FirebaseFunctions functions = FirebaseManager.Instance.Func;
public static void CreateNewCharacter(string text, Action<CharactersResponse> successCallback, Action<CharactersResponse> failureCallback)
{
Debug.Log("Preparing Function");
// Create the arguments to the callable function.
var data = new Dictionary<string, object>();
data["text"] = text;
// Call the function and extract the operation from the result.
HttpsCallableReference function = functions.GetHttpsCallable("CreateNewCharacter");
function.CallAsync(data).ContinueWithOnMainThread((task) =>
{
if (task.IsFaulted)
{
foreach(var inner in task.Exception.InnerExceptions)
{
if (inner is FunctionsException)
{
var e = (FunctionsException)inner;
// Function error code, will be INTERNAL if the failure
// was not handled properly in the function call.
var code = e.ErrorCode;
var message = e.Message;
Debug.LogError($"Code: {code} // Message: {message}");
if (failureCallback != null)
{
failureCallback.Invoke(new CharactersResponse()
{
Code = CharacterCode.UnknownError,
UnknownErrorMessage = $"ERROR: {code} : {message?.ToString()}"
});
}
}
}
}
else
{
Debug.Log("About to Deserialize response");
// PROBLEM IS HERE *********************
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
Debug.Log("Deserialized response");
if (response == null)
{
Debug.LogError("Response is NULL");
}
else
{
Debug.Log("ELSE");
Debug.Log($"Response: {response}");
Debug.Log(response.Code.ToString());
}
}
});
}
The problem :
In my Unity C# code, task.Result.Data contains the CharactersCode I've set in my function, but I can't find a way to convert it to CharactersResponse. (It worked in Azure Functions). Moreover, the line just after Deserialization Debug.Log("Deserialized response"); is not executed. The code seems stuck in the deserialization process.
I tried with and without extending my TypeScript class with CustomResponse(because I don't need it in my Function so I didn't extended it at first).
I also tried setting a CharacterID because I thought maybe it didn't like the fact that this property was missing but the result is the same.
I don't understand what is the problem here? If any of you can help.
Thanks.
HttpsCallableResult.Data is of type object!
=> Your ToString will simply return the type name something like
System.Object
or in your case the result is a dictionary so it prints out that type.
=> This is of course no valid JSON content and not what you expected.
Simply construct the result yourself from the data:
var result = (Dictionary<string, object>)task.Result.Data;
CharactersResponse response = new CharactersResponse
{
Code = (CharactersCode)(int)result["Code"],
CharacterID = (string)result["CharacterID"];
};
I wanted to implement derHugo's solution but couldn't find a way to convert task.Result.Data to Dictionary<string, object>.
The code was stuck at var result = (Dictionary<string, object>)task.Result.Data; even in step by step debugging and no error popped up.
OLD SOLUTION:
So I did a little research and stumbled upon this post and ended up using this instead :
var json = JsonConvert.SerializeObject(task.Result.Data);
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(json);
I basically convert the task.Result.Data to JSON and convert it back to CharactersResponse and it works. I have what I wanted.
However, I seem to understand that it is not the best solution performance-wise, but for now it is okay and I can now move forward in the project, I'll try to find a better solution later.
NEW SOLUTION:
I wanted to try one last thing, out of curiosity. I wondered what if I convert to JSON at the beginning (in my function) instead of at the end (in my Unity app). So I did this in my function's TypeScript code:
response.Code = CharactersCode.InvalidName;
var r = JSON.stringify(response); // Added this line
return r; // return 'r' instead of 'response'
In my C# code, I retried this line of code:
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
And it works ! I just needed to convert my object to JSON in my function before returning it. It allows me to "save" one line of code to process on the client side compared to the old solution.
Thanks derHugo for your answer as it helped me finding what I want.

Angular Http JSON response Mapping

I am trying to map a Http JSON Response to a Custom Interface in Angular / typescript. I have tried it in several ways but have not made it yet. The JSON object is not correctly mapped to the interface. The map attribute stays "undefined". If I print the data directly, the JSON data is output correctly - the problem is that I don't know how to access it. Here is my code:
export interface IMap<T> {
map: Map<string, Array<T>>;
}
The JSON answer looks like this. It is Map< String,List< ? >> in Java.
{
"somenumbers": [
20,
40
],
"somemorenumbers": [
71,
111
]
}
Now I tried to map it the following way:
public getValues(
paramList: Array<string>
): Observable<IMap<any>> {
const url = `url`;
let params = new HttpParams();
for (let s of paramList) {
params = params.append("params", s);
}
return this.http.get<IMap<any>>(url, { params });
}
In the configservice I subscribe to the Method. How do I map the Response correctly so that the attribute map in data isn't undefined and can be accessed correctly?
this.configService
.getValues(["somenumbers", "somemorenumbers"])
.subscribe((data: IMap<any>) => {
//outputs the JSON Data as Object{somenumbers: Array(2), somemorenumbers: Array(2), map: Map(0)}
console.error(data);
console.error(data.map);//map is undefined => ERROR
});
As you can see the map attribute is undefined. It is just a "map: Map(0)". Now... - How do I get the JSON stuff into the export interface? The map attribute should be filled with the associated values.
I appreciate any help! :)
If I understood correctly you're expecting that by adding <IMap<any>> to the get call it will then return you the response mapped to IMap. It doesn't, check this issue.
What you can do instead is use rxjs map to map the response yourself like so:
return this.http.get<IMap<any>>(url, { params }).pipe(
map((response) => {
// map the response here
})
);
I realized that I actually don't need the export interface and changed the code to the following. It took a while for me to get that x.y is in ts the same as x["y"]. Via response[parameter] I can access the attributes of the response Object dynamically - exactly what I needed.
public getValues(
paramList: Array<string>
): Observable<Map<string, Array<any>>> {
const url = `url`;
let params = new HttpParams();
for (let s of paramList) {
params = params.append("params", s);
}
return this.http
.get<any>(url, {
params
})
.pipe(
map(response => {
let toReturn = new Map<string, any[]>();
for (let parameter of paramList) {
toReturn.set(parameter, response[parameter]);
}
return toReturn;
})
);
}
The mapping works now! The JSON answer is still the same as in the question above.
this.configService
.getValues(["somenumbers", "somemorenumbers"])
.subscribe((data: Map<string, any[]>) => {
console.error(data);
});
Thanks for the help and links #M Mansour !

Calling the JSON correctly

I have a JSON array fetched from the service to the controller. I'm able to display the JSON array in the console. But when a specific item from the JSON, is called it display's undefined. So how do I call it correctly so that I can use it in my view.
Controller:
$scope.onViewLoaded = function() {
callingService.getdata($scope.datafetched);
}
$scope.datafetched = function(response) {
debugger;
if (response) {
$rootScope.mainData = response;
$scope.localizeddataTypes = getLocalizedCollection($scope.mainData);
}
}
$scope.editFunction = function(key) {
console.log($scope.mainData);
debugger;
console.log($scope.mainData.keyValue);
}
Here console.log($scope.mainData); display's the JSON array but console.log($scope.mainData.keyValue); is displayed as undefined. And my JSON looks like
{
keyValue: "1234DEF56",
animals: {
name:"dog",
color:"brown"
},
birds:{
name:"canary",
color:"yellow"
}
}
So, how do I overcome this problem and why do I get it as Undefined.
Just a curiosity stuff. I feel that the content in that variable is stored in string format and not JSON or JavaScript Object. Try this, and see if that works?
$scope.mainData = JSON.parse($scope.mainData);
console.log($scope.mainData.keyValue);

QML model link to JSON data

How to link a JSON stream to a QML model like you would do with angularJS?
In my QML, I have a Websocket object that receives data from a server:
WebSocket {
id: socket
url: "ws://3.249.251.32:8080/jitu"
onTextMessageReceived: { var jsonObject = JSON.parse(message) }
onStatusChanged:
if (socket.status == WebSocket.Error) { console.log("Error: " + socket.errorString) }
else if (socket.status == WebSocket.Open) { console.log("Socket open"); }
else if (socket.status == WebSocket.Closed) { console.log("Socket closed"); }
active: false
}
In this JSON I have something like:
{ items: [ "FOO", "BAR" ] }
Then I want to display two tabs, on titles FOO and the other, unsurprisingly, titled BAR.
This work great if I create a repeater that goes through the array in my model and create a tab for each entry:
TabView {
anchors.fill: parent
Repeater {
model: ListModel { id: tabs }
Tab {
title: Caption
Rectangle { color: "red" }
}
}
}
Up to now, this really looks like angularJs. I just need now to update my model (scope for angular) with the data received through the websocket.
For this, I have to add the tabs from my JSON to the ListModel as such:
...
onTextMessageReceived: {
var jsonObject = JSON.parse(message)
tabs.append(jsonObject.ITU.Modalities);
}
...
The problem is, each time I'll receive an update from JSON, tabs will be added. I don't want to clear the ListView each time, that would be time consuming I think. Is there a smart way to update the model from JSON smartly ? In angular, as they are both javascript structure, they are easy to merge. But here, I don't see an easy way.

Entity object fails when converting to JSON

I have been trying to get an Entity Framework model to convert to JSON to display in a web page. The Entity object is created fine but something fails when it is returned. Below is the code from my ASP.NET Web API project. By setting a breakpoint I can see the object collection is created just fine.
public class ClientsController : ApiController
{
public IEnumerable<Client> GetAllClients()
{
using (var context = new MyClientModel.MyEntities())
{
var query = context.Clients.Where(c => c.State == "CA");
var customers = query.ToList();
return customers;
}
}
}
Here is the HTML/Javascript code I use to call the ASP.NET Web API
<script>
var uri = 'api/clients';
$(document).ready(function () {
// Send an AJAX request
$.getJSON(uri)
.done(function (data) {
// On success, 'data' contains a list of products.
alert('Made it!'); // ** Never reaches here **
$.each(data, function (key, item) {
// Add a list item for the product.
$('<li>', { text: item }).appendTo($('#clients'));
});
});
});
</script>
I use Fiddler to view the response and it returns a .NET error that says ...
The 'ObjectContent' type failed to serialize the response body for content type 'application/json; charset=utf-8'."
and the inner exception message is ...
Error getting value from 'Patients' on 'System.Data.Entity.DynamicProxies.Client
Patients is a related entity in my model but I am confused why it would be an issue as I am only returning Client objects.
I found a solution that works, though I admit I am not sure exactly how it works. I added the line context.Configuration.ProxyCreationEnabled = false; to my method that returns the object collection and all my objects were returned. I got the code from the following SO Question - WebApi with EF Code First generates error when having parent child relation.
public class ClientsController : ApiController
{
public IEnumerable<Client> GetAllClients()
{
using (var context = new MyClientModel.MyEntities())
{
context.Configuration.ProxyCreationEnabled = false; // ** New code here **
var query = context.Clients.Where(c => c.State == "CA");
var customers = query.ToList();
return customers;
}
}
}