I have a query string: a=1&b=2&c[1]=3&c[2]=4 etc…
I want a NSDictionary where a => 1, b => 2, c => [3,4]. Notice that that the value for c is an array. It should also be able to handle something like c[1][2]=5 to make an array of arrays c => [[5]].
Of course I can do it myself by splitting on the & and =, but what about other cases such as arrays and arrays of arrays. I want a structured NSDictionary from a POST request queryString and do not want to rewrite the wheel if this already exists.
Are there any class/method, through Apple or 3rd party, that will parse a query string into a structured NSDictionary?
The Google Toolbox for Mac contains a GTMNSDictionaryURLArgumentsAdditions category on NSDictionary which may do what you want.
If you have control over the query string (on the client side), you could send an encoded plist which can be decoded directly into an NSDictionary.
It might be overkill in this case, but ParseKit is an open source tokenizing/parsing toolkit written in Cocoa for Cocoa applications:
http://parsekit.com
it may be of interest to you.
ParseKit is cleanly separated into two components: a Tokenizer and then a high-level parsing toolkit built on top of that. You could use either or both of these components to help with this kind of task. But again, ParseKit may be overkill for this relatively simple parsing task.
Related
I'll have to write a very customised CSV-like parser/decoder. I have looked for open source ones on Github, but not found any that fits my needs. I can solve this, but my question is if it would be a total violation of the key/value decoding, to implement this as a TopLevelDecoder in Swift.
I have keys, but not exactly key/value pairs. In CSV files, there is rather a key for each column of data,
There are a number of problem with the files I need to parse:
Commas are not only for separation of fields, but there are also commas within some fields. Example:
//If I convert to an array
Struct Family {
let name: String?
let parents: [String?]
let siblings: [String?]
}
In this example, both parents' names are within the same field, and needs to be converted into an array, and also the siblings field.
"Name", "Parents","Siblings"
"Danny", "Margaret, John","Mike, Jim, Jane"
In the case of the parents, I could have split that into two fields in a struct like
Struct Family {
let name: String?
let mother: String?
let father: String?
}
but with the Siblings field that doesn't work, since there can be all from zero to many siblings. Therefore I will have to use an array.
There are cases when I will split into two fields though.
All the files I need to parse are not strictly CSV. All of the files have tabular data (comma-or tab-separated), but some of the files have a few rows of comments (sometimes containing metadata) that I need to consider. Those files have a .txt extension, instead of .csv.
## File generated 2020-05-02
"Name", "Parents","Siblings"
"Danny", "Margaret, John","Mike, Jim, Jane"
Therefore I need to peek at the first line(s) to determine if there are such comments, and after that has been parsed I can continue to treat the rest of the file as CSV.
I plan to make it look like any Decoder, from the applications point of view, but internally in my decoder i can handle things like they were a key/value pair, because there is just one set of keys, and that is the first line in the file, if there are no comments in the beginning. I still want to use CodingKeys though.
What are your thoughts? Should I implement in as a decoder (actually TopLevelDecoder in Swift), or would that be an abuse of the idea of key/value decoding? The alternative is to implement this as a parser, but I have to handle several types of files (JSON, GraphQL, CSV and CSV-like files), and I think my application code would be a lot simpler if I could use Decoders for all the types of files.
For JSON there's no problem, since there is already a HSON decoder in Swift. For GraphQL it's not a problem either, because I can write a decoder with an unkeyed container. The problem files are those CSV and CSV-like files.
Some of them have everything in double-quotes, but for the "keys" in the CSV header and for the values. Some only have double-quotes for the keys, but not for the values. Some have comma-separated fields, and some tab-separated. Some have commas within fields, that needs special handling. Some have comments in the beginning of the file, that needs to be skipped, before parsing the rest of the file as CSV.
Some files have two fields in the first column. I have no influence whatsoever of the format of these files, so I just have to deal with it.
If you wonder what files they are, I can tell you that they are files of raw DNA, files with DNA matches, files with common DNA segments with people I have matching DNA with. It's quite a few slightly different files, from several DNA testing companies. I wish they all had used JSON in a standard format, where all keys also were standard for all the companies. But they all have different CSV headers, and other differences.
I also have to decode Gedcom files, which sort of also has key/value coded pairs, but that format too doesn't conform to a pure key/value coding in the files.
ALso: I have searched for others with similar problems, but not exactly the same, so I didn't want to hijack their threads.
See this thread Advice for going from CSV > JSON > Swift objects
That was more of a question of how to convert from CSV to JSON and then to internal data structs in Swift. I know I can write a parser to solve this, but I think it would be more elegant to handle all these files with decoders, but I want your thoughts about it.
I was also think of making a new protocol
protocol ColumnCodingKey: CodingKey {
)
I haven't decided yet what to have in the protocol, if anything.
It might work by just having it empty like in the example, and then let my decoder conform to it, then it maybe wouldn't be a very big violation of the key/value decoding.
Thanks in advance!
CSV files could be parsed using regular expression. To get you started this might save some time. It's hard to know what you really need because it looks like there are many different scenarios, it might grow to even more situations?
Regex expression to parse one line in a CSV file might look something like this
(?:(?:"(?:[^"]|"")*"|(?<=,)[^,]*(?=,))|^[^,]+|^(?=,)|[^,]+$|(?<=,)$)
Here is a detailed description on how it works with a javascript sample
Build a CSV parser
I'm maintaining a site written in GWT (2.5.0) that is used internally by our development team, and I've been experimenting with using AutoBeans for client side json parsing. I have a few objects with json that is not well defined — a developer can dump whatever json string he wants in there — so I'm using a Splittable property. In order to support editing this arbitrary json I'd like to convert a String into a Splittable, but I haven't found a straight-forward way of accomplishing this. Do I need to implement this interface myself or resort to something hacky like wrapping the json in another json object I can then decode into a throw-away AutoBean just to get a Splittable of the original json?
StringQuoter is the utility class which we do much of our manual Splittable work with.
Just user StringQuoter.create("some string"); to produce a Splittable whose payload is
"some string"
Once you have that splittable, you can assign it to a key in another splittable with the following method:
Splittable.assign(Splittable parent, String propertyName);
However, if you are trying to convert some arbitrary string which contains a JSON structure into a splittable, use StringQuoter.split(..) to create it. The resulting splittable can be queried as normal (i.e. what keys exist/don't exist, etc).
I'm looking for a convention on how to serialize my data when I have a (long) list of items that I want to POST to the server.
For example, if I have a resource /users and I wanted to POST a new one to it, I'd http-encode the fields for the new user and put it in the request body like this: name=foo&age=20
But if I have a list of users, like this [{ name: 'foo', age: 20 }, { name: 'bar', age: 10 }], is there a conventional way of POSTing this?
I'm thinking name[0]=foo&age[0]=20&name[1]=bar&age[1]=10 but I can't find anything to back it up. What do web servers usually accept/expect?
Quick question which may change my answer: Are you POSTing directly from an HTML form or are you expecting something more sophisticated (e.g. javascript processsing, or not even a web-based client)
If you have a sophisticated enough client, you could just construct a JSON string and POST with a content type of application/json. Then whatever resource is processing the POST could use any number of json libraries to read the posted string and process as is.
Further Rambling:
What framework/languages are you using to construct your REST service? Do they have built-in functionality/conventions to help you?
For example if you're using JAX-RS to build your service, there is a built in annotation #FormParam which can be used to process posted forms... for example: if you posted the following with a content type of application/x-www-form-urlencoded: name=foo&age=20&name=bar&age=10
You could retrieve parallel lists on the service side via:
#POST
#Consumes("application/x-www-form-urlencoded")
public void createUsers(#FormParam("name") List<String> name, #FormParam("age") List<String> age) {
// Store your users
}
But you would then have to deal with the question of what if one list is shorter/longer than the other, how do you resolve that? What happens if a new field is required or optional to create a list of users? (But as I mentioned initially, a JSON array of JSON objects would solve that issue... there are a number of libraries out there that support automagic JSON deserialization in JAX-RS or there is also the option of creating your own MessageBodyReader.
(Disclaimer on the next section: I don't know rails, my experience is more in the Java services world... I'm basing this on this guide). It looks like Rails has a convention of name[]=foo&name[]=bar to process posted data into arrays automagically, and a similar convention to populate structure like user[name]=foo&user[age]=20... Perhaps if you are on rails there is some way to use/abuse both of these features to get the desired result?
Other REST frameworks and languages may have their own conventions and functionality :)
Rails serializes forms on a format not unlike what you suggest. If you have a nested model it encodes it like this:
name=theo&company[name]=acme
(the equivalent JSON would be {"name": "theo", "company": {"name": "acme"}})
I can't say that I've seen a Rails application sending arrays, but there's no reason why it wouldn't work (worst case you would end up with a hash with string keys).
PHP has another convention, if you want to send an array you do
names[]=alice&names[]=bob&names[]=steve
But I don't know how you do nested objects that way.
The HTTP spec, or if it's the URI spec, not sure which atm, actually specifies that if you pass the same argument multiple times you get array of values (instead of the last-wins behaviour of most application frameworks). You can see this in the API docs for Jetty, for example: http://api.dpml.net/org/mortbay/jetty/6.1.5/org/mortbay/jetty/Request.html#getParameterValues(java.lang.String)
However, most of this applies to GET requests, not necessarily POST (but perhaps application/x-url-encoded should adhere to the same standards as GET).
In short, I don't think there is a standard for doing this, POST bodies are a bit of a wild west territory. I think, however, that either you should go with JSON, because it's made to describe structures, and application/x-url-encoded is not, or you should try to represent the structure of your data better, something like:
users[0][name]=foo&users[0][age]=20&users[1][name]=bar&users[1][age]=10
That has some kind of chance of actually being interpretable by a Rails app out of the box, for example.
Hey guys, I'm trying to parse HTML with XPath from http://lib.harvard.edu/libraries/hours.html in Objective-C for an application that shows the operating hours for each day of the week at each of the 50 libraries listed on the website. I found code to facilitate XPath parsing of HTML in Objective-C at cocoawithlove.com/2008/10/using-libxml2-for-parsing-and-xpath.html, but I'm still a little confused about how I should go about obtaining the hours for each day for each library. The relevant method to use seems to be
NSArray *PerformHTMLXPathQuery(NSData *document, NSString *query)
and my code so far is
NSURL *urlPath = [NSURL URLWithString:#"http://lib.harvard.edu/libraries/hours.html"];
NSArray *array = PerformHTMLXPathQuery([NSData dataWithContentsOfURL:urlPath], NSString *query);
but, since I've never used XPath before, I'm not sure what string I should use in the second parameter of the method. Does anyone have any ideas?
Also, I'm not quite sure what to do with the array that gets returned by PerformHTMLXPathQuery(). I feel like cocoawithlove.com/2008/10/using-libxml2-for-parsing-and-xpath.html gives a pretty good explanation, it's just that I've never used XPath before so it doesn't make much sense to me at this point. So, to summarize, as long as my code so far is correct, I want to know what to use for the second parameter in the PerformHTMLXPathQuery() method and how to extract the relevant data from the array it returns. Any help would be much appreciated!
XPath is a language for navigating XML documents. The query parameter is an XPath query string, which you hope will be able to extract the elements you want from the HTML file. I say "hope" because
I don't know how well XPath plays with HTML 4 documents
I've had a look at the source of the page you want to parse and it is quite complex.
Anyway, those points aside, you'll be wanting to learn how to create an XPath expression. Fortunately, Google is your friend and typing "XPath" into it brings up the W3Schools tutorial on XPath. I have only skimmed it but it looks like what you need.
I fetch a JSON array from a web service with touchJSON.
Which looks like this:
[{"icecream": {"title": "Banana"}}, {"icecream": {"title": "Strawberry"}}]
I'm not able to parse this into a NSDictionary, because
touchJSON doesn't support JSON arrays.
How do I get my JSON array into a NSDicitionary?
Regards
Have you consider trying another framework? This one seems to support JSON arrays.
Maybe you could use another of the many JSON implementations listed on the JSON homepage.
You can chekc out the JSON webpage, where they provide links to parsing code in dozens of languages. However, at first glance it looks like you're trying to munge from one type of object (the JSON Array) into another that might not be able to capture all the relationships (the NSDictionary). Full disclaimer: I've never used an NSDictionary before.