GET Working with JSON in Swift / Sudo Null IT News FREE


Show me a developer who has ne'er had to bring with JSON . Due to its simplicity, flexibility and visibility, this data presentment format has gained great popularity and is now used everywhere. So during the experiments with Swift, I quickly came across the call for to parse JSON data.

Really, the definitive Foundation garment API - NSJSONSerialization - perfectly copes with the task of head and inverse JSON conversion from a text histrionics to an object model . Apple has done some serious work to ensure that Swift and Objective-C code communicate directly and backward ( Using Fleet with Hot chocolate and Objective-C), therefore, the use of the familiar Cocoa API is not only possible in practice, but likewise convenient and does not look unnatural:

          let jsonString = "{\"name\":\"Toilet\",\"age\":32,\"phoneNumbers\":[{\"type\":\"home\",\"number\":\"212 555-1234\"}]}" let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) let jsonObject: AnyObject! = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(0), error: nil)                  

But working with the result in a statically typed Swift is inopportune - you need a chain of checks and type conversions to get any value. Next, I will see solutions to this problem, and at the same time we wish capture acquainted with some features of Swift.

Formally obtained direct NSJSONSerialization, the JSON mental representation consists of instances of Foundation types - NSNull, NSNumber, NSString, NSArray, NSDictionary. But runtime bridging provides sounding compatibility and interchangeableness of these types and the in proportion to Swift primitives - denotive types (Int, Double, etc.), String, Align, Dictionary. Therefore, in the Gustavus Franklin Swift code, we lavatory work with the resulting physical object "natively". Theorise we need to check the value of the first phone issue. On Objective-C, this might look like this:

          NSString *number = jsonObject[@"phoneNumbers"][0][@"number"]; NSAssert(["212 555-1234" isEqualToString:number], @"numbers should match");                  

Aside victimization self-propelled typewriting, navigating the hierarchy of a JSON aim does not cause problems in Objective-C. Swift, by contrast, uses strong static typewriting, indeed you must use expressed type casting at each step "deeper" in the pecking order:

          let person = jsonObject! as Dictionary              let phoneNumbers = person["phoneNumbers"] as Lay out                let phoneNumber = phoneNumbers[0] as Dictionary                  let add up = phoneNumber["count"] as String assert(number == "212 555-1234")                                                            

Unfortunately, the current version of the compiler (Xcode 6 important 2) generates an erroneous belief for this cypher - there is a problem with parsing expressions of explicit typewrite conversions with operands using subscripts . This can be circumvented through intermediate variables:

          let someone = jsonObject! arsenic Dictionary              let phoneNumbers : AnyObject? = somebody["phoneNumbers"] let phoneNumbersArray = phoneNumbers as Align                let phoneNumber : AnyObject? = phoneNumbersArray[0] countenance phoneNumberDict = phoneNumber as Dictionary                  get number : AnyObject? = phoneNumberDict["number"] have numberString = act as String assert(numberString == "212 555-1234")                                                            

This option works aright, but it sure enough looks awful. Aggregate acquiring the prise in one expression with optional downcasting and optional chaining :

          let maybeNumber = (((jsonObject as? NSDictionary)?["phoneNumbers"] as? NSArray)?[0] arsenic? NSDictionary)?["number"] as? NSString assert(maybeNumber == "212 555-1234")                  

Already better, but course it is difficult to shout out such a record spacious - for reading and especially for editing.

It is clear that this is only a specialised shell of a ordinary problem - the complexness of working with dynamic data structures in strongly typed languages. There are variant approaches to resolution this problem, some change the way the data is computerized. But for a specific case with parsing JSON objects, I wanted to find a simple solution that meets the following requirements:

  • concentration and readability of the code;
  • minimize the need for explicit typecasting;
  • the possibility and gismo of "disclosing" several levels of the hierarchy in one expression in a chain;
  • chuck out the possibility of runtime errors due to a mismatch of the JSON structure with expectations - in such cases, the value of the entire expression should be nil;
  • Minimize CPU and memory overhead.

First of all, I tried to find and study ready-to-wear solutions.

Enum representation

Several authors immediately suggest a related approach:

  • github.com/owensd/json-Swift
  • github.com/lingoer/SwiftyJSON
  • github.com/maxpow4h/swiftz/blob/master/swiftz/JSON.western fence lizard

Let U.S. briefly consider their device using json-swift as an example:

  1. A new enum typewrite ( github ) is introduced to represent JSON objects
  2. Using the associated values chemical mechanism, the possible values ​​of this enum are lot to crude Swift types corresponding to JSON types ( github )
  3. The important eccentric constructor checks the type of the passed object and returns an instance with the desired enum assess ( github )
  4. At the same time, for containers (arrays and dictionaries), elements are computerized recursively ( github )
  5. To navigate the JSON hierarchy, subscripts are implemented that return the appropriate elements for arrays and dictionaries ( github )
  6. For the inverse conversion from the JSON enumeration to the commensurate primitive Swift type, computed properties are used , which return the related value lone if the type matches ( github )

Transforming the original object model into such a representation, we get a convenient interface that can be accustomed pilot the power structure and get the value of the expected type:

          let number = JSON(jsonObject)?["phoneNumbers"]?[0]?["number"]?.string assert(number == "212 555-1234")                  

Here, the nonobligatory chaining mechanism provides a guarantee of the absence of runtime errors. When parsing an object with an inappropriate structure, the value of the integral expression will be nil (demur for the case when the indicant is accessed outside the array).

It turns out that such a solution meets whol the requirements put forward, except for one. When exploitation it, a mandatory algorithmic traversal of the smooth pecking order of the JSON targe occurs and a inexperient targe representation of the data is created. Naturally, in some cases, such overheads do non work a fundamental frequency role. But nevertheless, the whole solution cannot be called optimal in damage of using CPU and retentivity resources.

A 13 way to solve the problem would be to use so much an object JSON representation right at the arrange of rebirth from a text representation. But such an approach is already beyond the scope of the problem at issue - convenient work with the native physical object mental representation of JSON.

Lazy Processing

Another approach to solving the complete changeover trouble is to manipulation "lazy" logic to check and type cast when traversing JSON. Instead of immediately re-creating the entire JSON hierarchy with the values ​​of the required types, you can do this at each step "in-depth" - for lone one requested element. The execution of just such an approach is offered away the notorious Mike Ash: gist.github.com/mikeash/f443a8492d78e1d4dd10

Unfortunately, with this approach, it will not make up conceivable to present a separate JSON assess in the same handy form (enum + joint values). But such a solution is obviously more optimal. At first sight, there is also a small overhead in the form of creating an additional negligee object at apiece step late into the hierarchy. But these objects are defined as structures (struct Value), so their initialization and use can be considerably optimized away the encyclopedist in Swift.

Decision

I still sought-after to chance a answer that does not use newborn types, but extends the conduct of standard types as necessary. Rent out's consider a nigher calculate at the expression with standard syntax

          (((jsonObject as? NSDictionary)?["phoneNumbers"] as? NSArray)?[0] as? NSDictionary)?["number"] as? NSString                  

In fact, sole transitions to elements of dictionaries and arrays cause problems here. This is because the inferior ( [1]or ["number"]) call out imposes a requirement on the type of value to which IT is applied — in our case, we are leading to an NSDictionary or to an NSArray. Or, but then, the obtained values ​​from NSArray and NSDictionary are of type AnyObject, which requires a type cast to be used later in the song Sir Ernst Boris Chain.

It turns out that the need for a formed leave go away if we operate on a worldwide typecast that at the start supports both subscript options and returns objects of the same type. In Sceloporus occidentalis, the protocol formally corresponds to this definition:

          protocol JSONValue {     inferior(key: String) -> JSONValue? { get }     subscript(index: Int) -> JSONValue? { get } }                  

Thus, the protocol will watch the JSON time value, which can e'er be accessed past subscript (with an Int Beaver State String parameter). As a result, you can get either an element of the collection (if the objective is a collection, its type corresponds to the typewrite subscript and an element with such subscript is in the collection), OR nil.

To work with standard types in this way, you penury to ensure that they match JSONValue. Swift allows you to add protocol implementations through extensions . Atomic number 3 a result, the whole solution looks like this:

          protocol JSONValue {     subscript(#primal: String) -> JSONValue? { get }     subscript(#indicator: Int) -> JSONValue? { get } } extension NSNull : JSONValue {     subscript(#Francis Scott Key: String) -> JSONValue? { return nil }     subscript(#index: Int) -> JSONValue? { return nil } } extension NSNumber : JSONValue {     subscript(#key: String) -> JSONValue? { rejoinder nil }     subscript(#index: Int) -> JSONValue? { return nix } } extension NSString : JSONValue {     inferior(#key: String) -> JSONValue? { hark back nil }     subscript(#index finger: Int) -> JSONValue? { return nil } } extension NSArray : JSONValue {     subscript(#key: String) -> JSONValue? { return goose egg }     subscript(#forefinger: Int) -> JSONValue? { return exponent < count && index >= 0 ? JSON(self[indicant]) : nil } } extension NSDictionary : JSONValue {     subscript(#key: String) -> JSONValue? { return JSON(self[key]) }     inferior(#index: Int) -> JSONValue? { return cypher } } func JSON(object: AnyObject?) -> JSONValue? {     if let many : AnyObject = object {         switch some {         case let null as NSNull: return null         case let number as NSNumber: return numeral         case have string American Samoa NSString: return string         case Lashkar-e-Toiba array as NSArray: return regalia         case let dict as NSDictionary: return dict         default: income tax return nil         }     } else {         return nil     } }                  

A few notes:

  • to avoid conflict with standard subscripts in NSDictionary and NSArray, subscripts with named parameters are used - #key, #exponent;
  • a helper function is accustomed cast arbitrary values ​​to the JSONValue type, because the classic checking and cast operators work only for protocols well-marked with an attribute@objc (this is most belik referable the fact that correspondence to non- @objcprotocols hindquarters be added to much Swift types, data about which are not available in runtime);
  • although the cypher operates on Foundation types, runtime bridging ensures that Swift primitives work correctly.

As a result, we give notice use the construction to work with JSON:

          let maybeNumber = JSON(jsonObject)?[key:"phoneNumbers"]?[forefinger:0]?[key:"number"] as? NSString insist(maybeNumber == "212 555-1234")                  

Although this is non the most compact version of those advised, such a solution fully meets all of the above requirements.

Unconventional option

Based on the same estimate, a variant victimization a protocol with an @objcproperty is possible . This allows you to use explicit type molding or else of an auxiliary function, but prohibits the use of subscripts - you will stimulate to use ordinary methods or else. But these methods commode be declared as @optional:

          @objc protocol JSON {     @optional func array(indicant: Int) -> JSON?     @optional func object(key: String) -> JSON? } extension NSArray : JSON {     func array(index: Int) -> JSON? { return index < count && index >= 0 ? self[index finger] as? JSON : nil } } extension NSDictionary: JSON {     func object(central: Bowed stringed instrument) -> JSON? { return self[primal] as? JSON } } elongation NSNull : JSON {} extension NSNumber : JSON {} extension NSString : JSON {}                  

Usage example:

          countenance maybeNumber = (jsonObject as? JSON)?.object?("phoneNumbers")?.array?(0)?.object?("keep down") as? NSString assert(maybeNumber == "212 555-1234")                  

Non every bit compact as the option with subscripts. Somebody may be confused by the add up of question Simon Marks, but happening the other paw, each of its use is understandable and carries a semantic load.

In my opinion, the solutions found meet the given requirements and look preferable to the opposite options well-advised. And the idea used - the allocation of a universal protocol with methods that payof optional values ​​- can be put-upon to conveniently work non alone with JSON, but also with other mechanics information structures.

Encode and usage examples are free on github .

DOWNLOAD HERE

GET Working with JSON in Swift / Sudo Null IT News FREE

Posted by: salguerounnot1977.blogspot.com

0 Response to "GET Working with JSON in Swift / Sudo Null IT News FREE"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel