diff --git a/Text/XML/Enumerator/Cursor.hs b/Text/XML/Enumerator/Cursor.hs
--- a/Text/XML/Enumerator/Cursor.hs
+++ b/Text/XML/Enumerator/Cursor.hs
@@ -25,8 +25,10 @@
     , checkName
     , anyElement
     , element
+    , laxElement
     , content
     , attribute
+    , laxAttribute
     -- * Operators
     , (&|)
     , (&/)
@@ -37,12 +39,17 @@
     , ($//)
     , ($.//)
     , (>=>)
+    -- * Error handling
+    , force
+    , forceM
     ) where
 
 import           Control.Monad
-import           Data.List      (foldl')
+import           Data.Function                (on)
+import           Data.List                    (foldl')
 import           Text.XML.Enumerator.Resolved
-import qualified Data.Text      as T
+import qualified Control.Failure              as F
+import qualified Data.Text                    as T
 
 -- TODO: Consider [Cursor] -> [Cursor]?
 -- | The type of an Axis that returns a list of Cursors.
@@ -259,6 +266,11 @@
 element :: Name -> Axis
 element n = checkName (== n)
 
+-- | Select only those elements with a loosely matching tag name. Namespace and case are ignored. XPath:
+-- /A node test that is a QName is true if and only if the type of the node (see [5 Data Model]) is the principal node type and has an expanded-name equal to the expanded-name specified by the QName./
+laxElement :: T.Text -> Axis
+laxElement n = checkName (on (==) T.toCaseFold n . nameLocalName)
+
 -- | Select only text nodes, and directly give the 'Content' values. XPath:
 -- /The node test text() is true for any text node./
 -- 
@@ -280,3 +292,24 @@
                                             guard $ n == n'
                                             return v
 attribute _ _ = []
+
+-- | Select attributes on the current element (or nothing if it is not an element).  Namespace and case are ignored. XPath:
+-- /the attribute axis contains the attributes of the context node; the axis will be empty unless the context node is an element/
+-- 
+-- Note that this is not strictly an 'Axis', but will work with most combinators.
+-- 
+-- The return list of the generalised axis contains as elements lists of 'Content' 
+-- elements, each full list representing an attribute value.
+laxAttribute :: T.Text -> Cursor -> [T.Text]
+laxAttribute n Cursor{node=NodeElement e} = do (n', v) <- elementAttributes e
+                                               guard $ (on (==) T.toCaseFold) n (nameLocalName n')
+                                               return v
+laxAttribute _ _ = []
+
+force :: F.Failure e f => e -> [a] -> f a
+force e [] = F.failure e
+force _ (x:_) = return x
+
+forceM :: F.Failure e f => e -> [f a] -> f a
+forceM e [] = F.failure e
+forceM _ (x:_) = x
diff --git a/runtests.hs b/runtests.hs
--- a/runtests.hs
+++ b/runtests.hs
@@ -64,13 +64,18 @@
         , it "has correct checkName" cursorCheckName
         , it "has correct anyElement" cursorAnyElement
         , it "has correct element" cursorElement
+        , it "has correct laxElement" cursorLaxElement
         , it "has correct content" cursorContent
         , it "has correct attribute" cursorAttribute
+        , it "has correct laxAttribute" cursorLaxAttribute
         , it "has correct &* and $* operators" cursorDeep
+        , it "has correct force" cursorForce
+        , it "has correct forceM" cursorForceM
         ]
     , describe "resolved"
         [ it "identifies unresolved entities" resolvedIdentifies
         , it "works for resolvable entities" resolvedAllGood
+        , it "merges adjacent content nodes" resolvedMergeContent
         ]
     ]
 
@@ -194,6 +199,7 @@
         ,       "<bin2/>"
         ,       "<bin3/>"
         ,    "</bar3>"
+        ,    "<Bar1 xmlns=\"http://example.com\" Attr=\"q\"/>"
         , "</foo>"
         ]
 
@@ -210,11 +216,11 @@
   name (Cu.preceding baz2) @?= ["baz1", "bar1"]
   name (Cu.preceding bin2) @?= ["bin1", "baz3", "baz2", "baz1", "bar2", "bar1"]
 cursorFollowing = do
-  name (Cu.following baz2) @?= ["baz3", "bar3", "bin1", "bin2", "bin3"]
-  name (Cu.following bar2) @?= ["bar3", "bin1", "bin2", "bin3"]
+  name (Cu.following baz2) @?= ["baz3", "bar3", "bin1", "bin2", "bin3", "Bar1"]
+  name (Cu.following bar2) @?= ["bar3", "bin1", "bin2", "bin3", "Bar1"]
 cursorPrecedingSib = name (Cu.precedingSibling baz2) @?= ["baz1"]
 cursorFollowingSib = name (Cu.followingSibling baz2) @?= ["baz3"]
-cursorDescendant = (name $ Cu.descendant cursor) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3"
+cursorDescendant = (name $ Cu.descendant cursor) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3 Bar1"
 cursorCheck = null (cursor $.// Cu.check (const False)) @?= True
 cursorPredicate = (name $ cursor $.// Cu.check Cu.descendant) @?= T.words "foo bar2 baz3 bar3"
 cursorCheckNode = (name $ cursor $// Cu.checkNode f) @?= T.words "bar1 bar2 bar3"
@@ -224,12 +230,14 @@
     where f e = "bar" `T.isPrefixOf` Res.nameLocalName (Res.elementName e)
 cursorCheckName = (name $ cursor $// Cu.checkName f) @?= T.words "bar1 bar2 bar3"
     where f n = "bar" `T.isPrefixOf` nameLocalName n
-cursorAnyElement = (name $ cursor $// Cu.anyElement) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3"
-cursorElement = (name $ cursor $// Cu.element "baz2") @?= ["baz2"]
+cursorAnyElement = (name $ cursor $// Cu.anyElement) @?= T.words "bar1 bar2 baz1 baz2 baz3 bar3 bin1 bin2 bin3 Bar1"
+cursorElement = (name $ cursor $// Cu.element "bar1") @?= ["bar1"]
+cursorLaxElement = (name $ cursor $// Cu.laxElement "bar1") @?= ["bar1", "Bar1"]
 cursorContent = do
   Cu.content cursor @?= []
   (cursor $.// Cu.content) @?= ["a", "b"]
 cursorAttribute = Cu.attribute "attr" cursor @?= ["x"]
+cursorLaxAttribute = (cursor $.// Cu.laxAttribute "Attr") @?= ["x", "y", "q"]
 cursorDeep = do
   (Cu.element "foo" &/ Cu.element "bar2" &// Cu.attribute "attr") cursor @?= ["y"]
   (return &.// Cu.attribute "attr") cursor @?= ["x", "y"]
@@ -237,6 +245,14 @@
   (cursor $/ Cu.element "bar2" &// Cu.attribute "attr") @?= ["y"]
   (cursor $/ Cu.element "bar2" &/ Cu.element "baz2" >=> Cu.attribute "attr") @?= ["y"]
   null (cursor $| Cu.element "foo") @?= False
+cursorForce = do
+  Cu.force () [] @?= (Nothing :: Maybe Integer)
+  Cu.force () [1] @?= Just 1
+  Cu.force () [1,2] @?= Just 1
+cursorForceM = do
+  Cu.forceM () [] @?= (Nothing :: Maybe Integer)
+  Cu.forceM () [Just 1, Nothing] @?= Just 1
+  Cu.forceM () [Nothing, Just 1] @?= Nothing
 
 showEq :: (Show a, Show b) => Either a b -> Either a b -> Assertion
 showEq x y = show x @=? show y
@@ -252,3 +268,9 @@
     Res.toXMLDocument (Res.parseLBS_ xml P.decodeEntities)
   where
     xml = "<foo><bar/><baz/></foo>"
+
+resolvedMergeContent =
+    Res.documentRoot (Res.parseLBS_ xml P.decodeEntities) @=?
+    Res.Element "foo" [] [Res.NodeContent "bar&baz"]
+  where
+    xml = "<foo>bar&amp;baz</foo>"
diff --git a/xml-enumerator.cabal b/xml-enumerator.cabal
--- a/xml-enumerator.cabal
+++ b/xml-enumerator.cabal
@@ -1,5 +1,5 @@
 name:            xml-enumerator
-version:         0.3.3
+version:         0.3.4
 license:         BSD3
 license-file:    LICENSE
 author:          Michael Snoyman <michaels@suite-sol.com>, Aristid Breitkreuz <aristidb@googlemail.com>
@@ -28,6 +28,7 @@
                    , blaze-builder             >= 0.2      && < 0.4
                    , blaze-builder-enumerator  >= 0.2      && < 0.3
                    , transformers              >= 0.2      && < 0.3
+                   , failure                   >= 0.1      && < 0.2
     exposed-modules: Text.XML.Enumerator.Parse
                      Text.XML.Enumerator.Render
                      Text.XML.Enumerator.Document
