Skip to content

eNote-GmbH/BRCybertron

 
 

Repository files navigation

BRCybertron - セイバートロン

BRCybertron is an Objective-C framework for executing XSLT 1.0 transforms. It is implemented as a lightweight wrapper around libxslt and libxml.

BRCybertron has been designed for running on iOS, which does not provide libxslt. Thus libxslt (version 1.1.29) is statically compiled into BRCybertron. The goal of the project is to make it so you can remain in lovely Objective-C land without having to dig down, down, down into the libxslt/xml C APIs.

Robots Documents in Disguise

Here's a contrived example of how to execute an XSLT transform, using in-memory based XML and XSLT resources:

id<CYInputSource> input = [[CYDataInputSource alloc] initWithData:
                           [@"<input><msg>Hello, BRCybertron.</msg></input>" dataUsingEncoding:NSUTF8StringEncoding]
                           options:CYParsingDefaultOptions];

CYTemplate *xslt = [CYTemplate templateWithData:
                    [@"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' "
                     @"xmlns:xs='http://www.w3.org/2001/XMLSchema' "
                     @"exclude-result-prefixes='xs' version='1.0'>"
                     @"<xsl:output method='xml' encoding='UTF-8' />"
                     @"<xsl:template match='input'>"
                     @"<output>"
                     @"<msg><xsl:value-of select='msg'/></msg>"
                     @"<msg>More than meets the eye!</msg>"
                     @"</output>"
                     @"</xsl:template>"
                     @"</xsl:stylesheet>"
                     dataUsingEncoding:NSUTF8StringEncoding]];

// run transform, and return results as an XML string
NSString *result = [xslt transformToString:input parameters:nil error:nil];

At this point, result contains XML like:

<?xml version="1.0" encoding="UTF-8"?>
<output>
  <msg>Hello, BRCybertron.</msg>
  <msg>More than meets the eye!</msg>
</output>

XML parsing

A big part of any XSLT workflow involves parsing XML. Not only is the input to an XSLT transformation XML but the XSLT language itself is XML based. BRCybertron includes support for parsing XML documents via the CYInputSource API, and provides CYDataInputSource for parsing XML held in memory via an NSData object as well as CYFileInputSource for parsing XML from a file.

XSLT transformations

The CYTemplate class represents a parsed, reusable XSLT document. You can create instances from NSData objects:

// create from NSData instance
NSData *xsltData = ...;
CYTemplate *xslt = [CYTemplate templateWithData:xsltData];

or from a file:

// create from a file
NSString *pathToXsltFile = ...;
CYTemplate *xslt = [CYTemplate templateWithContentsOfFile:pathToXsltFile];

Once you have your template instance, you can run as many transformations on it as needed, by either transforming into a string:

id<CYInputSource> xml = ...;
NSError *error = nil;
NSString *result = [xslt transformToString:xml parameters:nil error:&error];

or to a file:

id<CYInputSource> xml = ...;
NSString *pathToOutputFile = ...;
NSError *error = nil;
[xslt transform:xml parameters:nil toFile:pathToOutputFile error:&error];

XSLT parameters

You can pass string parameters into the transformation, which will be available as top-level <xsl:param> elements in the XSLT document. Just pass a dictionary to the transform* methods, where the keys are the names of the parameters you want to pass in. For example, in the following XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
	exclude-result-prefixes="xs xml"
	version="1.0">
	<xsl:output method="html" version="5.0" encoding="UTF-8" indent="yes" />

	<xsl:param name="first-name"/>

	<xsl:template match="passage">
		<html>
			<body>
				<p>Hello, <xsl:value-of select="$first-name"/>.</p>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>

we can pass a first-name parameter like this:

id<CYInputSource> xml = ...;
NSError *error = nil;
NSString *result = [xslt transformToString:xml parameters:@{ @"first-name" : @"Bob" } error:&error];

and (given the proper input XML) would transform into:

<html>
  <body>
    <p>Hello, Bob.</p>
  </body>
</html>

xsl:import and xsl:include support

When using file-based XSL documents, both xsl:import and xsl:include statements using relative URLs will work as expected. When using a CYDataInputSource however, you can provide an explicit base URL from which to resolve relative URLs from. For example you could configure the base path to be a virtual file within the app's main bundle like this:

// obtain XSL as data from somewhere...
NSData *xslData = nil;

// create a base path to a virtual file named "data.xml" within the app bundle
NSString *basePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"data.xml"];
CYDataInputSource *xsl = [[CYDataInputSource alloc] initWithData:xslData basePath:basePath options:0];

// create templates instance
CYTemplate *tmpl = [[CYTemplate alloc] initWithInputSource:xsl];

More generally, however, you can make use of the CYInputSourceResolver API to resolve these resources as needed. The CYBundleInputSourceResolver class is provided for loading resources from a bundle. The previous example could be rewritten to use that class like this:

// obtain XSL as data from somewhere...
NSData *xslData = nil;

// create template instance
CYTemplate *tmpl = [CYTemplate templateWithData:xslData];

// add bundle resolver (using the main bundle)
tmpl.inputSourceResolver = [CYBundleInputSourceResolver new];

Entity resolving

Sometimes you might be faced with XML documents that refer to unresolvable entities, for example this document without any DTD:

<?xml version="1.0" encoding="UTF-8"?>
<content>
  <para>&copy; 2016 Bad XML Citizen</para>
</content>

The CYEntityResolver API allows entities such as &copy; to be handled at runtime in a simple way. The CYSimpleEntityResolver class provides a way to register simple entity values:

[[CYSimpleEntityResolver sharedResolver] addInternalEntities:@{@"copy" : @"&#169;"}];

By default the XML will be resolved so that the entities are preserved, but you can turn on entity substitution using the libxml flag XML_PARSE_NOENT like this:

NSData *data = [@"<content><para>&copy; 2016 Bad XML Citizen</para></content>"
                dataUsingEncoding:NSUTF8StringEncoding];
id<CYInputSource> input = [[CYDataInputSource alloc] initWithData:data
                           options:(CYParsingOptions)(XML_PARSE_NOENT)];

At this point, if you called asString:error: on input you'd get the following (notice how © appears):

<?xml version="1.0" encoding="UTF-8"?>
<content>
  <para>© 2016 Bad XML Citizen</para>
</content>

Sample app

The CreationMatrix project included in the source repository includes a sample application that you can use to test running XSLT transformations on your own data.

Project Integration

You can integrate BRCybertron via CocoaPods or manually as a dependent project.

via CocoaPods

Install CocoaPods if not already available:

$ [sudo] gem install cocoapods
$ pod setup

Change to the directory of your Xcode project, and create a file named Podfile with contents similar to this:

platform :ios, '7.1'
pod 'BRCybertron'

Install into your project:

$ pod install

Open your project in Xcode using the .xcworkspace file CocoaPods generated.

Note: CocoaPods as of version 0.39 might not produce a valid project for this pod. You can work around it by running pod like this:

$ COCOAPODS_DISABLE_DETERMINISTIC_UUIDS=YES pod install

About

Objective-C XSLT processor.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Objective-C 81.0%
  • C 11.1%
  • XSLT 6.0%
  • Ruby 1.9%