This is my function for parsing JSON:
func jsonParsingWeather(urlPath:String) -> NSDictionary {
var utf8URLPath = urlPath.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
println("URL path for JSON: \(utf8URLPath)")
var streamData:NSData = NSData(contentsOfURL: NSURL(string: utf8URLPath))
var error: NSError?
var fullWeatherDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(streamData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
println("Full parsing array: \(fullWeatherDictionary)")
return fullWeatherDictionary
}
Found problems in the processing of Cyrillic characters
urlPath = http://autocomplete.wunderground.com/aq?query=Самара
utf8URLPath = http://autocomplete.wunderground.com/aq?query=%D0%A1%D0%B0%D0%BC%D0%B0%D1%80%D0%B0
In the Safari browser, both rows give the same result:
{ "RESULTS": [ { "name": "Самара, Россия", "type": "city", "c": "RU", "zmw": "00000.1.28807", "tz": "Europe/Samara", "tzs": "SAMT", "l": "/q/zmw:00000.1.28807", "ll": "53.250000 50.450001", "lat": "53.250000", "lon": "50.450001" } ] }
But! Array fullWeatherDictionary is empty!
{
RESULTS = (
);
}
For comparison, take a latin string:
urlPath = http://autocomplete.wunderground.com/aq?query=Samara
utf8URLPath = http://autocomplete.wunderground.com/aq?query=Samara
In the Safari browser, both rows give the same result:
{ "RESULTS": [ { "name": "Samara, Russia", "type": "city", "c": "RU", "zmw": "00000.1.28807", "tz": "Europe/Samara", "tzs": "SAMT", "l": "/q/zmw:00000.1.28807", "ll": "53.250000 50.450001", "lat": "53.250000", "lon": "50.450001" }, { "name": "Samarate, Italy", "type": "city", "c": "IT", "zmw": "00000.101.16066", "tz": "Europe/Rome", "tzs": "CEST", "l": "/q/zmw:00000.101.16066", "ll": "45.625370 8.783435", "lat": "45.625370", "lon": "8.783435" } ...........
Accordingly, the array fullWeatherDictionary is:
{
RESULTS = (
{
c = RU;
l = "/q/zmw:00000.1.28807";
lat = "53.250000";
ll = "53.250000 50.450001";
lon = "50.450001";
name = "Samara, Russia";
type = city;
tz = "Europe/Samara";
tzs = SAMT;
zmw = "00000.1.28807";
},
.......................
What is wrong? Why different contents of the array fullWeatherDictionary, in the case of non-Latin characters?
The URL http://autocomplete.wunderground.com/aq?query=%D0%A1%D0%B0%D0%BC%D0%B0%D1%80%D0%B0 also gives the empty results in Safari.
It is a server issue.
Damn! Sorry, problem solved:
I configured the iOS Simulator on Russian language and region and everything was fine - the values in the array appeared in any language
Related
I am working on upgrading a service from ES 5.0 to 6.8. I have a bucket aggregate that in v5 serializes to this:
"items": [
{
"key": "random+topic",
"docCount": 27919,
"aggregations": {
"ParentReference": {
"docCount": 24992,
"aggregations": {
"Popularity": {
"value": 25223
}
}
}
}
},
{
"key": "unknown problem+latency",
"docCount": 24566,
"aggregations": {
"ParentReference": {
"docCount": 23419,
"aggregations": {
"Popularity": {
"value": 23931
}
}
}
}
},
With the v6 of Elasticsearch.Net and Nest, when serialized, I end up with:
"items": [
{
"ParentReference": {
"Popularity": {
"value": 25223
}
}
},
{
"ParentReference": {
"Popularity": {
"value": 23931
}
}
},
I had previously encountered the issue where the "aggregations" property is no longer returned (though I would love a pointer to the breaking changes announcement on that), and have updated my code accordingly. I can't do much without the Key and docCount, however. I figure there must be something related to the Json parsing changes.
I have already tried the steps in:
Custom Serialization | Elasticsearch.Net and NEST: the .NET clients [6.x] | Elastic
I have tried with the default serializer, as well as a custom one using the JsonNetSerializer.Default to no effect.
Can anyone provide a suggestion on what I should be doing?
note that this is how I am getting my BucketAggregate:
var childAgg = response.Aggregations[ss.Type] as SingleBucketAggregate;
var nestedAgg = childAgg.Aggregations[ss.Path] as SingleBucketAggregate;
var countAgg = nestedAgg.Aggregations[ssTermsName] as BucketAggregate;
return new ProviderResult<BucketAggregate>
{
Result = countAgg,
};
Thank!
~john
additional:
ElasticClient elasticClient_BuiltInSerializer = new ElasticClient();
var source = elasticClient_BuiltInSerializer.SourceSerializer.SerializeToString(o);
var response = elasticClient_BuiltInSerializer.RequestResponseSerializer.SerializeToString(o);
ConnectionSettings connectionSettings = new ConnectionSettings(new SingleNodeConnectionPool(new Uri("http://fake")), JsonNetSerializer.Default);
ElasticClient elasticClient_JsonNetSerializer = new ElasticClient(connectionSettings);
var source2 = elasticClient_JsonNetSerializer.SourceSerializer.SerializeToString(o);
var response2 = elasticClient_JsonNetSerializer.RequestResponseSerializer.SerializeToString(o);
I am using the following dataweave function, and it does works.
%dw 2.0
import * from dw::core::Arrays
output application/json
var mysqlInvoices = [
{
"id": 1,
"owner": "Joseph"
},
{
"id": 2,
"owner": "Maria"
}
]
var sapInvoices = [
{
"number": 3,
"issuedBy": "XYZ"
},
{
"number": 4,
"issuedBy": "ABC"
}
]
---
leftJoin(mysqlInvoices, sapInvoices, (m) -> m.id, (s) -> s.number) map (item, index) ->
(item.l mapObject (sItem, sKey) ->
(if ((sKey) as String == "id") "identifier"
else if ((sKey) as String == "owner") "ownerName"
else (sKey)): sItem)
++
(if (item.r != null)
item.r mapObject (sItem, sKey) ->
(sKey): sItem
else
sapInvoices[0] mapObject
(sItem, sKey) -> (sKey): "")
However, I am thinking if I can improve this function at two points:
change the key conditions:
I dont think that is the best practice to check every key match an if condition to change it:
(if ((sKey) as String == "id") "identifier"
else if ((sKey) as String == "owner") "ownerName"
else (sKey)): sItem
Use the original object to map it as an empty string when the leftJoin do not match keys:
sapInvoices[0] mapObject (sItem, sKey) ->
(sKey): ""
I am uncomfortable with these two points, and I believe that there are ways to improve this code, I just dont know how.
If there is a very different way of doing the same task, I also appreciate that kind of suggestion.
Based on George's answer, you can remove pluck and match and directly combine left and right table. See below:
%dw 2.0
import * from dw::core::Arrays
output application/json
var mysqlInvoices = [
{
"id": 1,
"owner": "Joseph"
},
{
"id": 2,
"owner": "Maria"
}
]
var sapInvoices = [
{
"number": 3,
"issuedBy": "XYZ"
},
{
"number": 4,
"issuedBy": "ABC"
}
]
var fs2rn = {
id: "identifier",
owner: "ownerName"
}
var rightEmpty= {number:"",issuedBy:""}
---
leftJoin(
// Do the field renaming at the very begining
mysqlInvoices map ($ mapObject {(fs2rn[$$] default $$): $}),
sapInvoices,
(m) -> m.identifier,
(s) -> s.number
) map (item) -> item.l ++ (item.r default rightEmpty)
Give the following a try, if anything the code seems a bit simpler:
%dw 2.0
import * from dw::core::Arrays
output application/json
var mysqlInvoices = [
{
"id": 1,
"owner": "Joseph"
},
{
"id": 2,
"owner": "Maria"
}
]
var sapInvoices = [
{
"number": 3,
"issuedBy": "XYZ"
},
{
"number": 4,
"issuedBy": "ABC"
}
]
var fs2rn = {
id: "identifier",
owner: "ownerName"
}
var rightEmpty= {number:"",issuedBy:""}
---
leftJoin(
// Do the field renaming at the very begining
mysqlInvoices map ($ mapObject {(fs2rn[$$] default $$): $}),
sapInvoices,
(m) -> m.identifier,
(s) -> s.number
)
// Iterate over the results
// Get just the values, and colapse the objects into a single object
map (
{($ pluck $)}
)
// Iterate over the results and use pattern-matching to
//
map (
$ match {
// Check if you have an id but not a number fields
// In which case add the rightEmpty object
case o if (o.identifier? and not (o.number?)) -> o ++ rightEmpty
// Or give the object because you now have both an id and a number
else o -> o
}
)
The features and functions I used are:
Dynamic Elements, documentation
pluck, documentation
Pattern-matching using the match operator, documentation
If I was to give you an advice, it would be to better indent your code. Nonetheless, pretty good job!
I am using ServiceNow develop instance to create a table, in which one field is type of list. The values of the list is populated by Outgoing Rest message.
In business rule, I make a hard coded to see how list values shall be what kind of form. The codes lists as below/1/: but when I update the field computer room, the computerlist's value is not populated. It looks like in the image/2/. How to populate values to a list type of field?
/3/ is the json string returned in pretty format. I try to populate hostName to computerlist, but failed.
/1/ scripts in business rule:
(function executeRule(current, previous /*null when async*/ ) {
try {
var r = new sn_ws.RESTMessageV2('x_383581_aiademo2.AIACmdbReq', 'Default GET');
var response = r.execute();
var responseBody = response.getBody();
var httpStatus = response.getStatusCode();
// var responseObj = JSON.parse(responseBody);
current.computerlist = new Array;
current.computerlist[0] = "c1";
current.computerlist[1] = "c2";
current.computerlist[2] = "c3";
current.assignto = "How to make it as list";
current.update();
} catch (ex) {
var message = ex.message;
}
})(current, previous);
/2/
/3/
{
"code": 200,
"data": {
"dataList": [
{
"hostName": "MysqlServer",
"deviceIp": "192.168.1.40",
"site": "SH",
"hostId": "00000000",
"location": "Room01",
"id": 9381947
},
{
"hostName": "192.168.1.32",
"deviceIp": "192.168.1.32",
"site": "SH",
"hostId": "a8c02001",
"location": "66666",
"id": 9381950
},
{
"hostName": "back-server",
"deviceIp": "192.168.1.42",
"site": "SH",
"hostId": "00000000",
"location": "Room01",
"id": 9381996
},
{
"hostName": "192.168.1.32",
"deviceIp": "192.168.1.32",
"site": "SH",
"hostId": "00-0C-29-E0-31-32",
"location": "Room01",
"id": 9382011
},
{
"hostName": "core-server1",
"deviceIp": "192.168.1.30",
"site": "SH",
"hostId": "00000000",
"location": "Room01",
"id": 9382014
}
]
},
"msg": "success"
}
/4/ business rule script is updated to:
(function executeRule(current, previous /*null when async*/ ) {
try {
var r = new sn_ws.RESTMessageV2('x_383581_aiademo2.AIACmdbReq', 'Default GET');
var response = r.execute();
var responseBody = response.getBody();
var httpStatus = response.getStatusCode();
var responseObj = JSON.parse(responseBody);
current.computerlist = responseObj.data.dataList.hostName;
current.assignto = responseObj.code;
current.update();
} catch (ex) {
var message = ex.message;
}
})(current, previous);
The answer is as follows:
(function executeRule(current, previous /*null when async*/ ) {
try {
var r = new sn_ws.RESTMessageV2('x_383581_aiademo2.AIACmdbReq', 'Default GET');
var response = r.execute();
var responseBody = response.getBody();
var httpStatus = response.getStatusCode();
var responseObj = JSON.parse(responseBody);
var listLength = responseObj.data.dataList.length;
current.responsetest = responseObj.data.dataList[0].hostName;
var temp = new Array(listLength);
for(var j = 0; j<listLength; j++){
temp[j] = responseObj.data.dataList[j].hostName;
}
current.computerlist.setValue(temp);
current.assignto = temp[0];
current.vers = "0.0.1a";
current.description = responseObj.msg;
current.update();
////////////////////////good way///////////////////////////////
///////////current.computerlist.setValue(["1","2","3","4","5"]);
///////////////////////////////////////////////////////////////
} catch (ex) {
var message = ex.message;
}
})(current, previous);
I would like to filter from REST API by nested data. For example this object:
[
{
"name": "Handmade Soft Fish",
"tags": "Rubber, Rubber, Salad",
"categories": [
{
"name": "women",
"id": 2,
"parent_id": 0,
"permalink": "/women"
},
{
"name": "kids",
"id": 3,
"parent_id": 0,
"permalink": "/kids"
}
]
},
{
"name": "Tasty Rubber Soap",
"tags": "Granite, Granite, Chair",
"categories": [
{
"name": "kids",
"id": 3,
"parent_id": 0,
"permalink": "/kids"
}
]
}
]
is comming by GET /api/products?filter[include]=categories
and i would like to get only products which has category name "women". How do this?
LoopBack does not support filters based on related models.
This is a limitation that we have never had bandwidth to solve, unfortunately :(
For more details, see the discussion and linked issues here:
Filter on level 2 properties: https://github.com/strongloop/loopback/issues/517
Filter by properties of related models (use SQL JOIN in queries): https://github.com/strongloop/loopback/issues/683
Maybe you want to get this data by the Category REST API. For example:
GET /api/categories?filter[include]=products&filter[where][name]=woman
The result will be a category object with all products related. To this, will be necessary declare this relation on the models.
Try like this.It has worked for me.
const filter = {
where: {
'categories.name': {
inq: ['women']**strong text**
}
}
};
Pass this filter to request as path parameters and the request would be like bellow
GET /api/categoriesfilter=%7B%22where%22:%7B%categories.name%22:%7B%22inq%22:%5B%women%22%5D%7D%7D%7D
Can you share how it looks like without filter[include]=categorie, please ?
[edit]
after a few questions in comment, I'd build a remote method : in common/models/myModel.js (inside the function) :
function getItems(filter, categorieIds = []) {
return new Promise((resolve, reject) => {
let newInclude;
if (filter.hasOwnProperty(include)){
if (Array.isArray(filter.include)) {
newInclude = [].concat(filter.include, "categories")
}else{
if (filter.include.length > 0) {
newInclude = [].concat(filter.include, "categories");
}else{
newInclude = "categories";
}
}
}else{
newInclude = "categories";
}
myModel.find(Object.assign({}, filter, {include: newInclude}))
.then(data => {
if (data.length <= 0) return resolve(data);
if (categoriesIds.length <= 0) return resolve(data);
// there goes your specific filter on categories
const tmp = data.filter(
item => item.categories.findIndex(
categorie => categorieIds.indexOf(categorie.id) > -1
) > -1
);
return resolve(tmp);
})
}
}
myModel.remoteMethod('getItems', {
accepts: [{
arg: "filter",
type: "object",
required: true
}, {
arg: "categorieIds",
type: "array",
required: true
}],
returns: {arg: 'getItems', type: 'array'}
});
I hope it answers your question...
I'm working with an API rest and it returns me two types of json, object and array.
Object(When there is only one record in the database):
{ "complain": { "id": "1" , "description": "hello", "date": "2017-01-24 11:46:22", "Lat": "20.5204446", "Long": "-100.8249097" } }
Array(When there is more than one record in the database):
{ "complain": [ { "id": "1" , "description": "hello", "date": "2017-01-24 11:46:22", "Lat": "20.587446", "Long": "-100.8246490" }, { "id": "2" , "description": "hello 2", "date": "2017-01-24 11:50:12", "Lat": "20.529876", "Long": "-100.8249097" } ] }
The code I use to consume the json is as follows:
content = await response.Content.ReadAsStringAsync();
var token = JToken.Parse(content);
if (token["complain"] is JArray)
{
var jsonArray = JsonConvert.DeserializeObject<RootArray>(content);
}
else if (token["complain"] is JObject)
{
var jsonObject = JsonConvert.DeserializeObject<RootObject>(content);
}
When it comes to a json array if I can add it to a listview:
myList.ItemsSource = jsonArray.listArray;
But if it is an object I can not and I get the following error:
Cannot implicitly convert type Object to IEnumerable.
Finally I was able to solve my error, it was just a matter of creating a list and adding the deserialized json object.
var jsonObject = JsonConvert.DeserializeObject<RootObject>(content);
List<Complain> simpleList = new List<Complain>();
simpleList.Add(jsonObject.ComplainObject);
myList.ItemsSource = simpleList;
The class to deserialize the Json object:
public class RootObject
{
[JsonProperty("complain")]
public Complain ComplainObject { get; set; }
}