NewtonSoft JSON converter serializes but does it weirdly. How to fix? - javascript

Given the following object
Friend Class GetLocationsResult
Public Property X1 As String
Public Property X2 As String
Public Property X3 As String
Public Property X4 As String
Public Property X5 As String
Public Property X6 As Double
Public Property X7 As Double
End Class
And it is declared and instantiated thusly:
Dim objList as List(of GetLocationsResults) = new List(of GetLocationsResults)
And objList is populated via an iterator that churns through a collection of objects/aggregate classes. The iterator just shoves values into a new GetLocationsResult object and then adds it to the list.
And given the NewtonSoft JSONConvert.SerializeObject(objList) results:
{"d":"[{\"X1\":\"Store Name\",\"X2\":\"Address\",\"X3\":\"City\",\"X4\":\"State\",\"X5\":\"Zip\",\"X6\":Coord1,\"X7\":Coord2}]"}
This point has been addressed and is no longer an issue
There are several issues with this result set. First, for whatever odd
reason, the object being named "d" is not acceptable.
How can I specify something other than "d" for the "name" of the json array?
When I attempt to JSON.parse the response, it needs to be in the following format in order to actually get to the data:
resultSet = JSON.parse(data.d);
console.warn(resultSet[0].X1);
Having to say resultSet[0] is, of course, not acceptable.
How do I cause the 'JSONConvert.Serialize' method to not wrap the response in such a way that I have to refer to the first index of the
resulting JSON data so that I can say resultSet.X1 instead of
resultSet[0].X1?
As requested, here is some more detailed information that may be relevant to the issue at hand.
The data is being returned from a WCF service. The exposed method is decorated thusly:
<WebInvoke(Method:="GET", ResponseFormat:=WebMessageFormat.Json)>
and returns type String. The service is being consumed by a desktop application and a mobile platform. The desktop website can have its own method and, in fact, does because we don't want to deal with X1, X2, etc while the mobile platform devs have declared that as necessary. As noted, the method returns a number of results in the form of a custom, aggregate class which is then shoved into a class object which is only a collection of properties. The return statement is thus:
Return JsonConvert.SerializeObject(retVal)
where retVal is a List(of GetLocationsResult)
So while having to access the data via index may be fine for the website, it is not acceptable for the mobile platform. Because each will run their own methods, it is possible to have a unique solution for both if needed.

Three things.
It sounds like you're returning that back through an ASP.NET ASMX
ScriptService or ASPX [WebMethod]. When using one of those
endpoints, you don't need to (and shouldn't) manually serialize the
object into JSON. ASP.NET will do that for you automatically. You don't need to use JSONConvert at all; just return your object as the result. That's why you're seeing all that escaped data after the .d. It's JSON serialized twice.
As WhiteHat mentioned, the .d is a good thing in some situations. It's introduced by default in these ASP.NET JSON services and can't easily be removed on the server-side, but is easy to account for when you parse the response on the client-side.
You are returning a List(of GetLocationResults), which maps to a JavaScript array. That's why you need to use resultSet[0].X1 to access a value in the result. If you want to access that value via resultSet.X1, you need to be sure to return only a single GetLocationResults object instead of a List of them.
Does that help? If you could update your question with a more complete example of your server-side and client-side code, I could give you an example of how to address the preceding three issues.

Related

How to compare int as string?

I'm using Realm in my React Native app.
There is a search bar to filter data by ID, and that ID is int in my database.
If user searches for 418, I need to return all IDs like 41878, 41835.
Here is some code:
findAll({ search }) {
let items = this._realm.objects<Data>(this._schema.name)
if (search.length)
items = items.filtered(`id = '${search}'`)
return items
}
As expected, this code only returns the item that have the exact ID 418.
I tried something like
items.filtered(`id CONTAINS '${search}'`)
But gives me:
Error: Unsupported comparison operator 'contains' against type 'int', right side must be a string or binary type
I can't use Array's filter because it will make the Realm execute the query and slow down the code.
How can I achieve this without triggering the Realm to run the query?
CONTAINS is not supported on Int types.
There are a few solutions:
One is to keep two properties in your model, one an Int and one a String version of that Int. You could implement that pretty easily through an init function within the object that assigns the Int and then makes a string version for the string property
Another is to store all of the values as Strings. You'll just need to Int them if sorting or doing calculations. You can then use CONTAINS

java pass string web request to non string #RequestParam

I am working on a react application that has a java backend. The react frontend sends a URL request to a java web service controller, and within that webservice controller, the variable types are defined. Since a URL request is a string I’m having trouble dealing with non-string values. And since Javascript is not a strongly-typed language like Java I cannot definie variable types on the front end.
Web Request Example:
localhost:8080/filteredCSV.json?status=UNDER_ACTIVE_REVIEW,AUTHORIZED_BY_DWB,UNDER_CONSTRUCTION,CONSTRUCTION_COMPLETED&engineerEmail=null&issues=null&date=null
Web Service Controller:
#RequestMapping(value = "/filteredCSV.json")
#ResponseBody
public WebserviceResponse<?>filteredCSV(
#RequestParam(value="status") ArrayList status,
#RequestParam(value="engineerEmail") ArrayList engineerEmail,
#RequestParam(value="issues", required=false) Boolean issues,
#RequestParam(value="date", required=false) Local Date date){
return service.filteredCSV(status, engineerEmail, issues, date);
}
If the Boolean or Date values are null, then null is getting passed as a string which causes a TypeMismatch Error and the program stops. I do not appear to have a way to change the string to to a non-string null value once it hits the web service controller. I understand why this is occurring, but I am not sure if there is a way to work around it so that I can pass null as a value and not null as a string. Am I just screwed? Is there not a way to deal with this?
You're not sending null as a value, you're sending a string that contains the word 'null.
Send no value "" instead of the string value 'null'. This is what the #RequestParameter is expecting when it should be ignoring a parameter.
#RequestParameter docs
If you can't edit the javascript you still have some options ... what spring is really doing there is data binding values from the http request to instances of Java variables. You can override or bypass this as you need. (This sort of approach is the 'old' way of doing this and the very problem spring annotations hope to solve).
#RequestMapping(value = "/filteredCSV.json")
#ResponseBody
public WebserviceResponse<?>filteredCSV(HttpRequest request){
String date = request.getParameter('date');
// Convert date if not 'null' here, and so on
}
I like the suggestion posted by #zmf, However If want find other work arounds. Please refer some of the approaches I think will work:
Solution 1: You can introduce a filter, and parse all the request parameters as string. When you encounter null as string do not send the param further, it will be considered as null at controller level.
Solution 2: Replace RequestParam type like Date, Boolean with String. You can then process accordingly.

Return the result of a method in a QuerySet.values() or values_list()?

When calling .values() or .values_list() on a QuerySet you can pass in the specific fields you want returned, or even fields you want from related tables. What I'm wanting to do is include the result of a method defined on the model, in addition to some fields, for example:
class MyModel(models.Model):
name = models.CharField()
def favouriteNumber(self):
return random.randint(1,10)
list(MyModel.objects.all().values('name','favouriteNumber'))
=> [{'name':'Bob', 'favouriteNumber':6}, {'name':'Fred', 'favouriteNumber':4}]
The above doesn't work, but is what I'm wanting to do, in the same way that the templating language can treat model methods without parameters just like fields.
The reason I'm wanting to do this is so that I can pass the information to the JavaScript in the front end to use instead of making calls back to the server to retrieve it each time.
Assuming the above (or something similar) isn't possible, I know I could loop over my values() result afterwards and add in the extra information manually.. what would be the most efficient (and clean) way to do that?
You might want to try using only() instead of values(). Like values() it will only fetch the specified fields from the database, but unlike values() it will return actual instance objects that you can call methods on.
That way, whether you're looping in the view or the template, you can access the method in addition to the specified fields.
If you need dictionary-like structures (for json.dumps(), say) you can just construct them in a comprehension:
instances = MyModel.objects.only('name')
data = [{'name': instance.name,
'favourite': instance.favouriteNumber()} for instance in instances]

Breezejs automatic GUID parsing

I am using Breezejs in 'NoDB' mode, meaning I write my metadata by hand. When I create a Breeze query with OData parameters I add a filter by id, say
new breeze.Predicate('iD', datacontext.breeze.FilterQueryOp.Equals, myId)
The var myId is indeed a GUID value (though it's defined as a String), but in my DB and in both my server-side and client-side model it's a string (I can't change the DB structure). the property definition in my metadata model is
dataProperties: {
...
iD: { dataType: DataType.String },
...
}
(I know the property name looks weird, but I have to use this syntax since I have the breeze.NamingConvention.camelCase.setAsDefault() on my datacontext, and the property's name on the DB is ID uppercased)
When I execute the query I see that the corresponding oData filter option in the WebAPI url is like
$filter=ID eq guid'65BEB144-5C0C-4481-AC70-5E61FDAA840D'
which leads me to this server error: No coercion operator is defined between types 'System.Guid' and 'System.String'.
Is there a way to disable this automatic 'parsing' of GUIDs and leave them as strings?
I have temporarily solved this by removing the parsing directly inside breeze's source code so that my webAPI call would look like
$filter=ID eq '65BEB144-5C0C-4481-AC70-5E61FDAA840D'
but I don't like this solution and I would be glad if there was a better one, like parametrize this behaviour in some way. I didn't find anything about this on Breeze's official website.
Breeze uses its metadata to determine that datatype of each property in a query and then uses this information to generate the correct OData filter. So your metadata definition of ID as a string should be correct.
However, in order to perform this operation breeze needs to know the EntityType of your query. For example in the following query
var q = EntityQuery.from("Foo").where(....)
breeze needs to know the EntityType that "Foo" ( a resourceName) corresponds to. Once it has the entity type it can correctly format any filters for specific properties of this entityType. If breeze does not have 'EntityType', then it falls back to guessing about the datatype of each property. In your case, its guessing that the datatype is a 'Guid'
So the fix is to either tell the query directly about the EntityType that you are querying
var q = breeze.EntityQuery.from("Foo).where(....).toType(FoosEntityType);
or you can handle it more globally via via the MetadataStore.setEntityTypeForResourceName method.
breeze.MetadataStore.setEntityTypeForResourceName("Foo", FoosEntityType);
var q = breeze.EntityQuery.from("Foo).where(....); // your original query

How Do You Fix A Parameter Names Mismatch - DOJO and PL/SQL

How do you fix a names mismatch problem, if the client-side names are keywords or reserved words in the server-side language you are using?
The DOJO JavaScript toolkit has a QueryReadStore class that you can subclass to submit REST patterned queries to the server. I'm using this in conjunction w/ the FilteringSelect Dijit.
I can subclass the QueryReadStore and specify the parameters and arguments getting passed to the server. But somewhere along the way, a "start" and "count" parameter are being passed from the client to the server. I went into the API and discovered that the QueryReadStore.js is sending those parameter names.
I'm using Fiddler to confirm what's actually being sent and brought back. The server response is telling me I have a parameter names mismatch, because of the "start" and "count" parameters. The problem is, I can't use "start" and "count" in PL/SQL.
Workaround or correct implementation advice would be appreciated...thx.
//I tried putting the code snippet in here, but since it's largely HTML, that didn't work so well.
While it feels like the wrong thing to do, because I'm hacking at a well tested, nicely written JavaScript toolkit, this is how I fixed the problem:
I went into the DOJOX QueryReadStore.js and replaced the "start" and "count" references with acceptable (to the server-side language) parameter names.
I would have like to handled the issue via my PL/SQL (but I don't know how to get around reserved words) or client-side code (subclassing did not do the trick)...without getting into the internals of the library. But it works, and I can move on.
As opposed to removing it from the API, as you mentioned, you can actually create a subclass with your own fetch, and remove start/count parameters (theoretically). Have a look at this URL for guidance:
http://www.sitepen.com/blog/2008/06/25/web-service-data-store/
Start and count are actually very useful because they allow you to pass params for the query that you can use to filter massive data sets, and it helps to manage client-side paging. I would try to subclass instead, intercept, and remove.
Is your pl/sql program accessed via a URL and mod_plsql? If so, then you can use "flexible parameter passing" and the variables get assigned to an array of name/value pairs.
Define your package spec like this...
create or replace package pkg_name
TYPE plsqltable
IS
TABLE OF VARCHAR2 (32000)
INDEX BY BINARY_INTEGER;
empty plsqltable;
PROCEDURE api (name_array IN plsqltable DEFAULT empty ,
value_array IN plsqltable DEFAULT empty
);
END pkg_name;
Then the body:
CREATE OR REPLACE PACKAGE BODY pkg_name AS
l_count_value number;
l_start_value number;
PROCEDURE proc_name (name_array IN plsqltable DEFAULT empty,
value_array IN plsqltable DEFAULT empty) is
------------
FUNCTION get_value (p_name IN VARCHAR) RETURN VARCHAR2 IS
BEGIN
FOR i IN 1..name_array.COUNT LOOP
IF UPPER(name_array(i)) = UPPER(p_name) THEN
RETURN value_array(i);
END IF;
END LOOP;
RETURN NULL;
END get_value;
----------------------
begin
l_count_value := get_value('count');
l_start_value := get_value('start');
end api;
end pkg_name;
Then you can call pkg_name.api using
http://server/dad/!pkg_name.api?start=3&count=3

Categories

Resources