NSJSONSerialization and non-latin (cyrillic) characters in Swift - utf-8

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

"Key" on a BucketAggregate is missing when the response is serialized

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);

improve leftJoin nested mapObject when equivalence do and do not match

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!

ServiceNow, How to populate value to a list variable

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);

loopback REST API filter by nested data

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...

How to assign a single object to a list?

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; }
}

Resources