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"}
I'm using flowable and try to pass a JSON as body, but it's seen as malformed when processing the request (or so I think since the error is Bad Request). Basically I'm passing some parameters this way:
#PostMapping(path = PathConstants.START_ACTION)
public ResponseEntity<BaseResponse<ProcessInstance>> start(#PathVariable String processDefinitionId,
#RequestBody(required = false) Map<String, Object> params)
The params are set using postman, this way:
{
"body": {
"email":"testmail#test",
"password":"password"
}
}
The process starts and the POST call is made, but Bad Request is given back. I've tried printing the variables of the process after this call and this is what I have:
body={email=testmail#test, password=password}
So I've tried passing this instead:
{
"body": "{ \"email\":\"testmail#test\", \"password\":\"password\"}"
}
And when printing the variables I have:
body={"email":"testmail#test", "password":"password"}
but still it's a bad request. What is wrong with this JSON?
If you want to pass a variable that is a JSON then you would need to make sure that body is type JsonNode from Jackson.
Looking at your request signature Map<String, Object>, Jackson would contain a map of maps.
I don't know what you are trying to do. However, I would highly advise you to work with predefined parameters in your REST API. If you need something generic you can use the REST API of Flowable to do what you want to do.
I am trying to take console output from a json request and post it as a text label for the user. I can't seem to get it to work. Any advice? Thanks! It prints fine in the console, but won't work for the "self.resultLabel.text = json"
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
print(json)
dispatch_async(dispatch_get_main_queue(),{
self.myActivityIndicator.stopAnimating()
self.myImageView.image = nil;
self.resultLabel.text = json
func loadSites(){
//trying to get the post to show up in the place of the invisible label
}
});
}catch {
print(error)
}
I am not sure why exactly you want to show raw json to users in UILabel but you should convert json to a string if you want to assign it to UILabel's text property.
There is a description method, which is always used in Objective-C whenever you print an NSObject subclass via NSLog.
This method is also available in Swift:
self.resultLabel.text = (json as AnyObject).description
I just want to retrieve the html source from a simple website.
#IBAction func scan_func(sender: AnyObject) {
Alamofire.request(.GET, "http://www.example.com")
.response { request, response, data, error in
print(request)
print(response)
print(data)
}
}
I already have successfully added "App Transport Security Settings" and "Allow Arbitrary Loads" to info.plist to load http content.
When I run that code I only get an output like this:
XCODE - hexadecimal output
I hope you can help me.
kind regards
You are printing out the raw bytes of the data. I'm guessing you are looking for a way to print out the corresponding string representation.
You could for instance do this with Alamofire's provided closure:
Alamofire.request(.GET, "http://www.example.com")
.responseString { response in
print("Response String: \(response.result.value)")
}
(Check out the documentation here)
Alternatively, you could convert the string yourself:
Alamofire.request(.GET, "http://www.example.com")
.response { request, response, data, error in
print(String(data: data, encoding: String.Encoding.utf8))
}
Apple String reference documentation
I am attempting to build a REST service using Netty on the backend. I need to be able to post raw JSON to the service outside of any key/value parameters. Content-type=applicaiton/json not form multi-part.
I am able to get the initial part of the service to receive the request, but when I cast the MessageEvent content to HTTPRequest, it no longer has any posed data associated with it. That leaves me with no ability to get the JSON data back.
In order to access the posted JSON, do I need to use a different process for extracting the data from the MessageEvent?
Here is the snippet in question.
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
System.out.println("The message was received");
HttpRequest request = (HttpRequest) e.getMessage();
if (request.getMethod() != POST) {
sendError(ctx, METHOD_NOT_ALLOWED);
return;
}
// Validate that we have the correct URI and if so, then parse the incoming data.
final String path = sanitizeUri(request.getUri());
decoder = new HttpPostRequestDecoder(request);
System.out.println("We have the decoder for the request");
List<InterfaceHttpData> datas = decoder.getBodyHttpDatas();
for (InterfaceHttpData data : datas){
System.out.println(data.toString());
}
What am I missing that it causing this? Do I need to use the ChunkedWrite portion? I am a noob to Netty so forgive me if this is basic. I found lots of other questions about posting raw JSON to other URL's from inside Netty, but nothing about receiving it.
I've only used HttpPostRequestDecoder to read application/x-www-form-urlencoded or mime data.
Try just reading the data directly form the request as per the snoop example.
ChannelBuffer content = request.getContent();
if (content.readable()) {
String json = content.toString(CharsetUtil.UTF_8);
}