© 2012 James Montgomerie
[email protected], http://www.blog.montgomerie.net/
[email protected], http://th.ingsmadeoutofotherthin.gs/
- YACYAML reads and writes YAML, a friendlier, more human, plain text replacement for plists or JSON.
- YACYAML also works as a drop-in replacement for NSKeyedArchiver and NSKeyedUnarchiver in most situations, and writes a human-readable, less proprietary format.
- YACYAML is for iOS and Mac OS X.
- YACYAML decodes native Cocoa objects from YAML.
- YACYAML encodes native Cocoa objects to YAML.
- You don't need to know what YAML is to use YACYAML. Knowing what plain text is helps though.
- Use
YACYAMLKeyedUnarchiver
to read YAML, or the-YACYAMLDecode
methods onNSString
orNSData
objects holding YAML strings to decode them. - Call
-YACYAMLEncodedString
(or-YACYAMLEncodedData
) on Cocoa objects to get a plain-text YAML encoding. It'll work on all objects you could store in a plist or in JSON, and any others that supportNSCoding
. - Use
YACYAMLKeyedArchiver
to encode object graphs, as a replacement forNSKeyedArchiver
. - Use
YACYAMLKeyedUnarchiver
to decode object graphs encoded withYACYAMLKeyedArchiver
.YACYAMLKeyedUnarchiver
is toYACYAMLKeyedArchiver
asNSKeyedUnarchiver
is toNSKeyedArchiver
. - See below for how to compile and link YACYAML into your Mac or iOS project.
Read more at http://www.blog.montgomerie.net/yacyaml
YAML is a human friendly data serialization standard. YAML is a friendlier superset of JSON. YAML is easy for humans to read and write.
In spirit, YAML is sort of to data representation what Markdown is to text markup.
This is a simple dictionary represented in YAML. It would decode as an NSDictionary. But you can probably guess that, because YAML is designed to be easy for humans to read.
Just imagine how gigantic this would be as a plist.
date: 2012-04-01
etextNumber: 62
title: A Princess of Mars
author: Edgar Rice Burroughs
picker: Joseph
pickerIsReader: y
synopsis: >-
This first book in the Barsoom series follows American John Carter as he
explores Mars after being transported there by forces unknown. Carter
battles with gigantic Tharks, explores the red planet and chases after
Dejah Thoris, Princess of Helium. A influential work of sci-fi that has
inspired writers & readers for nearly a century.
A more complex example, with nested elements. This is an excerpt from Eucalyptus' catalog of books. I hope you'll agree it's nicer than a plist. I also think it's nicer than JSON (which, incidentally, couldn't represent this structure fully, because it uses integers - 62
in this excerpt - as dictionary keys):
62:
title: A Princess of Mars
creator: Burroughs, Edgar Rice, 1875-1950
description: Barsoom series, volume 1
file:
extent: 148804
format:
- text/plain; charset="us-ascii"
- application/zip
modified: 2012-03-26
url: http://www.gutenberg.org/dirs/6/62/62.zip
language: en
lcc: PS
lcsh:
- Science fiction
- Carter, John (Fictitious character) -- Fiction
- Dejah Thoris (Fictitious character) -- Fiction
- Mars (Planet) -- Fiction
- Princesses -- Fiction
rights: http://www.gutenberg.org/license
This is a JSON almost-equivalent - I've tried to indent is as readably as I can:
{
"62": {
"title": "A Princess of Mars",
"creator": "Burroughs, Edgar Rice, 1875-1950",
"description": "Barsoom series, volume 1",
"file": {
"modified": "2012-03-26",
"url": "http://www.gutenberg.org/dirs/6/62/62.zip",
"format": [
"text/plain; charset=\"us-ascii\"",
"application/zip"
],
"extent": 148804
},
"language": "en",
"lcc": "PS",
"lcsh": [
"Science fiction",
"Carter, John (Fictitious character) -- Fiction",
"Dejah Thoris (Fictitious character) -- Fiction",
"Mars (Planet) -- Fiction",
"Princesses -- Fiction"
],
"rights": "http://www.gutenberg.org/license"
}
}
An array of strings:
[[NSArray arrayWithObjects:@"one", @"two", @"three", nil] YACYAMLEncodedString];
- one
- two
- three
Also valid is, for example, [one, two, three]
A dictionary of strings:
[[NSDictionary dictionaryWithObjectsAndKeys:@"one", @"onekey",
@"two", @"twokey",
@"three", @"threekey",
nil] YACYAMLEncodedString];
onekey: one
twokey: two
threekey: three
You could also write, for example, { onekey: one, twoKey: two, threeKey: three }
And, to show that this can indeed archive arbitrary Cocoa objects (that implement NSCoder), here's a good old Mac OS NSButton. You wouldn't, of course, write this by hand, but I think there's value in having it stored in a human-readable format rather than what NSKeyedArchiver emits.
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(20, 20, 400, 50)];
button.buttonType = NSPushOnPushOffButton;
button.bezelStyle = NSBezelBorder;
button.title = @"If you wanna come to my house, then click me with your mouse.";
NSString *yaml = [YACYAMLKeyedArchiver archivedStringWithRootObject:button];
&a !NSButton
NSNextResponder:
NSvFlags: 256
NSFrame: '{{20, 20}, {400, 50}}'
NSTag: -1
NSEnabled: y
NSCell: !NSButtonCell
NSCellFlags: 67239424
NSCellFlags2: 134217728
NSContents: If you wanna come to my house, then click me with your mouse.
NSSupport: &b !NSFont
NSName: LucidaGrande
NSSize: 12
NSfFlags: 4880
NSControlView: *a
NSButtonFlags: -1232977665
NSButtonFlags2: 2
NSAlternateImage: *b
NSKeyEquivalent: ''
NSPeriodicDelay: 400
NSPeriodicInterval: 75
If you want to know more, Wikipedia has a lot of good YAML examples and explanations on its page on YAML, or you could read up on the esoteric details at the home of the YAML spec (you really don't need to just to use it though).
YACYAML uses YAML's 'anchors' to store repeated objects only once, and refer to them later. You can see an example of this in the encoded NSButton, above - it uses an anchor named 'a', and one named 'b'. By default, repeated strings are stored repeatedly, for human-readability, but you can change that behaviour if you want smaller, but less human-readable output. Check out YACYAMLKeyedArchiver
's YACYAMLKeyedArchiverOptionAllowScalarAnchors
option.
YACYAML is designed to be built as an static library and used directly by Xcode, as a subproject to your project. (like this). It should work correctly on any OS that supports Automatic Reference Counting (your project doesn't need to use ARC itself though - non-ARC code interoperates perfectly well with libraries that use it).
How to set up your iOS or Mac project to build and use YACYAML:
- Copy the YACYAML directory into, or (better) clone a YACYAML repository as a Git submodule in, your app project's directory hierarchy somewhere.
- In your app's Xcode project, drag the YACYAML.project into the navigator tree on the left.
- In your app's Xcode target settings, in the Build Phases section:
- Under Target Dependencies, press the '+' button, and add the YACYAML target from the YACYAML project.
- Under Link Binary With Libraries, press the '+' button, and add libYACYAML.a the YACYAML project.
- Under Link Binary With Libraries, press the '+' button, and add libresolv.dylib from your target SDK (libresolv provides Base64 encoding, used by YACYAML when reading and writing NSData objects).
- In your app's target settings, in the Build Settings section:
- Make sure All, not Basic is selected at the top.
- On the Header Search Paths line, add
"$(TARGET_BUILD_DIR)/usr/local/lib/include"
and"$(OBJROOT)/UninstalledProducts/include"
(make sure to include the quotes!) - On the Other Linker Flags line, make sure the flags
-ObjC
and-all_load
are there (add them if they're not). - If your project's not already using ARC, you'll also need to add
-fobjc-arc
to the Other Linker Flags (don't worry, that won't make Xcode think your project is using ARC too, it just links in the required support libraries for it so that YACYAML can use it).
- When you want to use YACYAML, just
#import <YACYAML/YACYAML.h>
.
It should hopefully parse everything 'sensibly', but specific mappings from/to Cocoa objects (and C basic types, when using the appropriate NSCoder methods) to/from YAML language-independent types exist for:
- Sequences (
!!seq
) to/fromNSArray
s - Mappings (
!!map
) to/fromNSDictionary
s - Sets (
!!set
) to/fromNSSet
s
- Strings (
!!str
) to/fromNSString
s - Numbers and booleans (
!!int
,!!float
,!!bool
) to/fromNSNumbers
s (and equivalent basic types). - Timestamps (
!!timestamp
) to/fromNSDate
s - Binary data (
!!binary
) to/fromNSData
s - Null (
!!null
) to/fromNSNull
- Pairs (
!!pairs
)- You'll get an array of dictionaries containing one key and value each (this is actually compliant with the spec, given that Cocoa has no pair class).
- Ordered mappings (
!!omap
)- You'll get an ordered array of dictionaries containing one key and value each (this is, again, compliant with the spec given that Cocoa has no ordered mapping class).
- Merging of mappings (
!!merge
)- You'll get mappings with a '<<' key representing the merge.
- Default values for mappings (
!!value
)- You'll get mappings with '=' key representing the default value. This may be spec-compliant, depending on how you think about default values.
- YAML in YAML (
!!yaml
)
YACYAML stood for Yet Another Cocoa YAML, but I think it deserves better than that now.
- why the lucky stiff for his Syck YAML parser, and Will Thimbleby for his Cocoa extensions to Syck. Syck's now sadly rather old and somewhat busted, but it's what originally got me using YAML.
- Kirill Simonov for libyaml, which YACYAML uses to parse and emit raw YAML, and without which I don't think I'd have contemplated this.
- Mike Ash for his old MAKeyedArchiver, which illuminated some things, and made me feel not so bad about how this decodes object cycles.