Swift JSON apostrophe ' string interpreted strangely on web server - json

POST a string to an endpoint (formatted in JSON), which looks something like this:
let rawString = reportJSON.rawString(.utf8, options: .prettyPrinted)!
let newParams = ["report": rawString]
APIHelper.shared.session.request(endPoint,
method: .post,
parameters: newParams,
encoding: URLEncoding.default)
print(rawString) looks like this in the Xcode console:
...
"remarks" : "Door is broke. It’s time to fix this door’s handle.",
...
Webserver raw log shows the single quotes ' are incorrectly parsed:
...
"remarks" : "Door is broke. ItÆs time to fix this doorÆs handle.",
...
But then I did a test by I manually writing a string:
var rawString = "{\"someKeyValue\": \"It's another string with apostrophe's.\"}"
Web server correctly receives and prints:
{"someKeyValue": "It's another string with apostrophe's."}
So is something is funky with my reportJSON.rawString() object? Is it sending something up to the server that is different than what is printed in my Xcode console log? Or could it be the webserver?

Related

GetCapabilities Query for TileServer Returns Malformed JSON

I have installed TileServer.php. When I navigate to it, I can see my tiles (so it's working).
My issue is when I query for the getCapabilities file the resulting json file is malformed.
The json is prefixed with part of the query string at the start of the json response.
Here is the full query string:
http://<=my ip=>/tileserver/index.html?service=wmts&request=getcapabilities&version=1.0.0
Actual Json Response I Receive
(Notice wmts&request is prefixed to the otherwise valid json)
====JSON===============================
wmts&request([{"name":"190322","type":"overlay","description":"190322","version":"1.1","format":"png","bounds":[174.92249449474565,-36.991878207885335,174.93635413927785,-36.98244705946717],"maxzoom":22,"minzoom":14,"basename":"1313_190322","profile":"mercator","scale":1,"tiles": ...
==================================================
I have tried removing part of the query string to test for the results, oddly enough it grabs the part of the query string again.
Here is the full query string I tested with:
http://<=my ip=>/tileserver/index.html?request=getcapabilities&version=1.0.0
(Actual Json Response I Receive)
====JSON===============================
getcapabilities&version([{"name":"190322","type":"overlay","description":"190322","version":"1.1","format":"png","bounds":[174.92249449474565,-36.991878207885335,174.93635413927785,-36.98244705946717],"maxzoom":22,"minzoom":14,"basename":"1313_190322","profile":"mercator","scale":1,"tiles": ...
=======================================================
I could parse this out I suppose but I would like to find the cause for this issue.
I am using ASP.Net 5.0.
Here is roughly my code:
private static readonly string _tileserver_ip = "http://<my ip>/tileserver/";
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var query = new Dictionary<string, string>
{
["service"] = "wmts",
["request"] = "getcapabilities",
["version"] = "1.0.0"
};
var response = await client.GetAsync(QueryHelpers.AddQueryString(_tileserver_ip, query));
var capabilitiesString = await response.Content.ReadAsStringAsync();
// the result of the query string => "http://<my ip>/tileserver/?service=wmts&request=getcapabilities&version=1.0.0"
EDIT
Opps! Turns out I was requesting the getCapabilities file from the TileServer in the completely wrong way.
I will leave this here encase it helps someone in the future.
Here is the correct URL: http://<= my url =>/tileserver/1.0.0/WMTSCapabilities.xml/wmts
I found the answer and I will leave this post here encase it helps someone in the future.
In my URL I was using index.html as the index page, however I should have been using index .json instead.
As soon as I switched to .json I received the JSON response as I was expecting.
Full URL with query string:
http://<=my ip=>/tileserver/index.json?service=wmts&request=getcapabilities&version=1.0.0

Swift - Parsing JSON with Combine, URLSession DataTaskPublisher - Strange formatting

I'm looking for some guidance here on an issue I'm encountering. I am polling an API for stock data and instead of getting back an array or formatted JSON I get chunks of it. Each block of JSON seems to be properly formatted, but the JSON decoders are looking for a properly formatted array.
Prior to using combine I had a different working. I had to parse the data to remove 4 trailing characters, then I was left with lines of JSON that I could split by newline and then do a forEach loop on to parse.
This was messy but it works. The issue now is I'm polling streaming data for live quote updating in my UI. I need to have the JSON chunks read into my class, stored as a variable that will update the UI. I am not worried about all of that code once I can figure out how to get the data read properly.
I know my app is receiving the data because I see the stats in the debugger for network. I also test the same API call in a browser and see what data my app is receiving. I just don't know how to break it up in the combine framework.
I have been working with something like this so far:
return URLSession.shared.dataTaskPublisher(for: request)
.map { $0.data }
.handleEvents(receiveSubscription: { print("Receive subscription: \($0)") },
receiveOutput: { print("Receive output: \($0)") },
receiveCompletion: { print("Receive completion: \($0)") },
receiveCancel: { print("Receive cancel") },
receiveRequest: { print("Receive request: \($0)") })
//.decode(type: CryptoDataContainer.self, decoder: JSONDecoder())
.decode(type: Quote.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
The Quote struct is very basic:
struct Quote: Codable {
public var Symbol: String
public var Last: Double
public var Bid: Double
public var Ask: Double
public var Close: Double
}
JSON data is posted below, and it streams. I get a few chunks of it at a time, but if I can split by newline or by everything {.*} to get each JSON chunk and decode individually it would work great.
I'm really looking for Last price and Symbol out of the JSON. I could even just look for Last and start one individual streaming session for each symbol I need.
I will also need to split these up using a subject to multicast each publisher to multiple subscribers but I wanted to get the basic functionality working before complicating matters.
Update: From another post I found that the problem is the JSON formatting - The API is returning invalid JSON that is not formatted into an array - it is missing the surrounding []. This is why it doesn't parse. Another option may be to simply convert to string, add square brackets around all of the JSON passed down from the server (there is nothing but objects).. and then parse it correctly? I'm not sure how to do this within a combine map but that seems to be the path forward.
Here’s an example of the map I’m trying to use currently, since the data comes in as json chunks I need to parse them each individually.
.map ({
let stringRepresentation = String(data: $0, encoding: .utf8)
let outputJson = stringRepresentation!.split(whereSeparator: \.isNewline).map(String.init)
if outputJson.count > 1 {
print("DEBUG: outputJson.count > 1 code running..")
let formattedOutputJson:String = outputJson.joined(separator: ",")
let finalJson:String = "{\"quotes\":[" + formattedOutputJson + "]}"
let data:Data? = finalJson.data(using: .utf8)
return data!
} else {
print("DEBUG: outputJson.count = 1 (Else) code running..")
let finalJson:String = "{\"quotes\":[" + outputJson[0] + "]}"
let data:Data? = finalJson.data(using: .utf8)
return data!
}
})
Here is a better example of the JSON formatting in chunks I’m talking about:
{"heartbeat":4,"timestamp":"\/Date(1622898090819)\/"}
{"heartbeat":5,"timestamp":"\/Date(1622898095819)\/"}
{"heartbeat":6,"timestamp":"\/Date(1622898100819)\/"}
{"heartbeat":7,"timestamp":"\/Date(1622898105819)\/"}
{"heartbeat":8,"timestamp":"\/Date(1622898110819)\/"}
If I can even capture the heartbeat number as an Int to use I would be well on my way to getting my code working. The issue is I get zero data out of this. I know I’m connected from packet captures and various other tests. The data is getting to my system, and to the Xcode simulated app, I just don’t think I’m getting it to a point where I can print to the console correctly to display it. I have been working on that prior to attempting to update the UI directly.
{"Description":"E-Mini NASDAQ-100 Jun 2021","PreviousClose":13529.25,"PreviousVolume":552987,"AssetType":"FUTURE","CountryCode":"United States","Halted":false,"Bid":13513.75,"BidSize":3,"Ask":13514.25,"AskSize":1,"Currency":"USD","LastSize":1,"LastVenue":"CME","SymbolRoot":"NQ","Exchange":"CME","LastTradingDate":"2021-06-18","DisplayType":3,"FractionalDisplay":false,"MinPrice":1258200,"PointValue":20,"TradeTime":"2021-06-04T02:40:20.0000000+00:00","MaxPrice":1447600,"MinMove":25,"High52Week":14064,"Open":13531.75,"Low52Week":9591.75,"High":13541.75,"Low":13467.75,"Close":13513.75,"Symbol":"NQM21","Volume":23877,"DailyOpenInterest":231736,"ExpirationDate":"2021-06-18","Last":13513.75,"NameExt":"(D)","DataFeed":"TradeStation","IsDelayed":true,"Restrictions":[null],"VWAP":13499.877658013,"NetChange":-15.50,"NetChangePct":-0.1145665872091948925476282900,"High52WeekTimeStamp":"2021-04-29T00:00:00.0000000+00:00","Low52WeekTimeStamp":"2020-06-11T00:00:00.0000000+00:00","PreviousClosePriceDisplay":"13529.25","BidPriceDisplay":"13513.75","AskPriceDisplay":"13514.25","MinPriceDisplay":"1258200.00","MaxPriceDisplay":"1447600.00","High52WeekPriceDisplay":"14064.00","OpenPriceDisplay":"13531.75","Low52WeekPriceDisplay":"9591.75","HighPriceDisplay":"13541.75","LowPriceDisplay":"13467.75","ClosePriceDisplay":"13513.75","LastPriceDisplay":"13513.75","VWAPDisplay":"13499.88","FirstNoticeDate":"","StrikePrice":0,"TickSizeTier":0,"Underlying":"","StrikePriceDisplay":"0.00"}
{"Last":4188.5,"NameExt":"(D)","DataFeed":"TradeStation","IsDelayed":true,"Restrictions":[null],"VWAP":4184.92506004117,"Description":"E-mini S&P 500 Jun 2021","PreviousClose":4191.25,"PreviousVolume":1384271,"AssetType":"FUTURE","CountryCode":"United States","Halted":false,"Bid":4188.25,"BidSize":39,"Ask":4188.5,"AskSize":23,"Currency":"USD","LastSize":1,"LastVenue":"CME","SymbolRoot":"ES","Exchange":"CME","LastTradingDate":"2021-06-18","DisplayType":3,"FractionalDisplay":false,"MinPrice":3898,"PointValue":50,"TradeTime":"2021-06-04T02:40:10.0000000+00:00","MaxPrice":4484,"MinMove":25,"High52Week":4238.25,"Open":4190.75,"Low52Week":2977.8999,"Hi{"Volume":23880,"TradeTime":"2021-06-04T02:40:23.0000000+00:00","VWAP":13499.879455674,"Symbol":"NQM21"}
{"AskSize":18,"Symbol":"ESM21"}
{"Bid":13514.25,"BidSize":1,"AskSize":1,"BidPriceDisplay":"13514.25","Symbol":"NQM21","VWAP":13499.8800582884,"Volume":23881,"TradeTime":"2021-06-04T02:40:25.0000000+00:00"}
{"VWAP":4184.92521199492,"AskSize":19,"Volume":47807,"TradeTime":"2021-06-04T02:40:27.0000000+00:00","Symbol":"ESM21"}
{"Bid":13514,"BidSize":3,"BidPriceDisplay":"13514.00","Symbol":"NQM21","TradeTime":"2021-06-04T02:40:27.0000000+00:00","Ask":13514.75,"VWAP":13499.8806713352,"Close":13514.5,"Last":13514.5,"Volume":23882,"NetChange":-14.75,"NetChangePct":-0.1090230426668144945211301400,"AskPriceDisplay":"13514.75","ClosePriceDisplay":"13514.50","LastPriceDisplay":"13514.50"}
{"AskSize":18,"Symbol":"ESM21"}

JSON couldn’t be read from URL because it isn’t in the correct format [duplicate]

This is a really weird bug, when grabbing JSON from my server (which is produced via PHP), I get this error when calling:
json = [NSJSONSerialization JSONObjectWithData:kivaData
options:kNilOptions
error:&jsonError];
JSON Error: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (Garbage at end.) UserInfo=0x178467d00 {NSDebugDescription=Garbage at end.}
My (NSData* kivaData) grabs everything perfectly, but it cant parse the JSON.
I have run my JSON code in http://jsonlint.com/ and it comes out Valid everytime.
Its really weird because it can parse the JSON when I connect to Wifi, but when I try doing it via cellular, it wont work. It does work over cellular on some peoples phones, but every time.
using swift 4, first of all check the JSON Data using print :
print (String(data:data!, encoding: .utf8)!)
check for the white spaces or unwanted characters, then remove them :
var string = String(data: data!, encoding: .utf8)
string = string?.replacingOccurrences(of: "/r/n", with: "")
after that, assign the string back to data variable :
let data1 = string!.data(using: .utf8)
encoding is very important. If your json is valid, the issue might be you have special characters in your json data, which is not correctly parsed by the json serializer. When you send the data, make sure you have the correct url-encoding when sending content so client will parse it correctly. Using utf-8 always or base64.
I was able to solve the same problem (works on wifi, but not on carrier network) by sending a content-length header just before the response:
header("Content-length: ".strlen($response));
echo $response;
exit;
I ended up having to change my php file from echoing the json syntax to simply outputting with json_encode.
JsonData is usually stored in dictionary format. Since the json is not able to parse the continuous data[its not able to separate the responses] its throwing this error .
You can maintain a dictionary to store the responses obtained from server .
Each task will have a unique response . So create a dictionary with "keys" as "taskIdentifier" of tasks and "values" as "data".
Eg:
Inside didReceiveData or any other equivalent methods [where you get response from server ] store response in dictionary with taskIdentifier as keys .
NSString *taskID = [#(dataTask.taskIdentifier) stringValue];
[_task_data_dictionary setObject:data forKey:taskID];
Here _task_data_dictionary is the dictionary.In this way you can get rid of the above error .
After this you can get data using the same dictionary using this code
NSData *data = [_task_data_dictionary objectForKey:taskNumber];
again using the taskIdentifier .
Hope this helps .

Alamofire string parameter hardcoded works but passing as string parameter does not

I'm attempting to make a Alamofire service call to retrieve a list of items in JSON. The issue I am having is that anytime I type in a special character: such as ' it somewhere resolves the string a unicode string while sending the request. When I type in o'sheas its coming back that I'm searching O\U2019sheas
func sendGetRequest(passedInString: String) {
PARAMETERS["searchTxt"] = passedInString
Alamofire.request(URL, method: .get , parameters: PARAMETERS, headers: HEADER)
.validate(statusCode: 200..<400)
.responseJSON { response in
debugPrint(response.request!)
switch response.result {
// GETTING NO RESULTS BECAUSE THE REQUEST IS TURNING the o'sheas into O\U2019sheas
But the odd thing is, if I just replace this:
PARAMETERS["searchTxt"] = passedInString
with a hardcoded string (the one I'm typing initially and getting no results)
PARAMETERS["searchTxt"] = "o'sheas"
...it works just fine and does not convert this to O\U2019sheas. Any idea how to fix this, and more importantly, why this is happening when passed in as a String parameter as opposed to hard coded string?
UPDATE:
I've tried adding the URLEncoding.default as the encoding parameter, same results.
I've also tried creating a Swift String Extension to be used on the searchTxt as passing it as parameter like so:
PARAMETERS["searchTxt"] = passedInString.escapeSpecialCharacters()
extension String {
mutating func escapeSpecialCharacters() {
// 1. Define replacements
let replacementCharacters = ["'" : "\'"]
// 2. Cycle through each replacement defined in the replacementCharacters array and remodify the string accordingly.
replacementCharacters.keys.forEach {
// 3. Replacing self with new modified string
self = self.replacingOccurrences(of: $0, with: replacementCharacters[$0]!)
}
}
}
But same results.
If you are sure about this Unicode problem, then you can use Encoder and Decoder functionality to handle this.
Or if you know the your String always will be same, then use Regex expression to append '\'
I think the problem is with the "get" - cause it will use your string in the URL as parameter, the "'" can't be handelt so well.
Try to create a url with URLComponents
var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItem]
then use urlComponents.url in
Alamofire.request(urlComponents.url, method: .get, headers: HEADERS) {...}
URLQueryItem(name: "Param", value: "eter")
Try below lines of code. May help you.
originalString = "o'sheas"
var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
PARAMETERS["searchTxt"] = escapedString

Parsing JSON with SwiftyJSON

I'm having trouble parsing the following JSON file with SwiftyJSON. I've looked around the web and tried different suggested solutions with no luck.
Here is the JSON:
{'info-leag':{'Status':1,'Name':'Testing Name','url-lig':'test.testing.com','uid':'12345'}}
And my relevant code:
//initializes request
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.currentQueue()) { response, maybeData, error in
if let data = maybeData {
let json = JSON(data: data)
//stores data as UTF8 String
let contents = NSString(data:data, encoding:NSUTF8StringEncoding)
The first part seems to work fine, I am able to get the JSON and save it as data, at the bottom I converted it to a string to make sure that I was getting the right information, I then later print it to make sure.
I tried different things like:
let name = json["info-league"]["Name"] //can't seem to get the context
I'm trying to get the Name and uid to be saved as 2 strings as well as the Status as an int.
Thanks!
Once you've made your JSON valid like this:
{"info-league":{"Status":1,"Name":"Testing Name","url-lig":"test.testing.com","uid":"12345"}}
you will be able to use your example, it works (I just tested):
let name = json["info-league"]["Name"]
but it's better to use SwiftyJSON types:
let name = json["info-league"]["Name"].string
let status = json["info-league"]["Status"].int
so your variables are of known types for later use.
If you don't do this they will be of type JSON, a type created by SwiftyJSON, and you will have to cast them later (not a problem, depends how you're organised in your code).
Try:
let name = json["info-league"]["Name"].string