diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,39 @@
 # ChangeLog for yaml
 
+## 0.11.11.2
+
+* Compat with aeson 2.2
+
+## 0.11.11.1
+
+* For optparse-applicative-0.18: use `pretty` instead of `text` [#216](https://github.com/snoyberg/yaml/pull/216)
+
+## 0.11.11.0
+
+* Fix ambiguous occurrence `AesonException`
+
+## 0.11.10.0
+
+* Undo previous change (breakage with aeson 2)
+
+## 0.11.9.0
+
+* Data.Yaml.Pretty: provide key-sorting function with path to parent object [#206](https://github.com/snoyberg/yaml/pull/206)
+
+## 0.11.8.0
+
+* Export `Parse` and `StringStyle` [#204](https://github.com/snoyberg/yaml/pull/204)
+
+## 0.11.7.0
+
+* Support `aeson` 2 [#202](https://github.com/snoyberg/yaml/pull/202)
+
+## 0.11.6.0
+
+* `yaml2json`: add `--help` and `--version` options [#197](https://github.com/snoyberg/yaml/pull/197)
+* `json2yaml`: add `--help` and `--version` options [#198](https://github.com/snoyberg/yaml/pull/198)
+* Add the `-o` options to both `yaml2json` and `json2yaml` [#200](https://github.com/snoyberg/yaml/pull/200)
+
 ## 0.11.5.0
 
 * New functions capable of parsing YAML streams containing multiple documents into a list of results:
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 ## yaml
 
-[![Build Status](https://travis-ci.org/snoyberg/yaml.svg?branch=master)](https://travis-ci.org/snoyberg/yaml)
-[![Build status](https://ci.appveyor.com/api/projects/status/hqy2jketp8m502so/branch/master?svg=true)](https://ci.appveyor.com/project/snoyberg/yaml/branch/master)
+[![GitHub build and test status](https://github.com/snoyberg/yaml/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/snoyberg/yaml/actions/workflows/tests.yml)
+[![Appveyor build status](https://ci.appveyor.com/api/projects/status/hqy2jketp8m502so/branch/master?svg=true)](https://ci.appveyor.com/project/snoyberg/yaml/branch/master)
 
 Provides support for parsing and emitting Yaml documents.
 
@@ -16,3 +16,11 @@
 * `Data.Yaml.Include` supports adding `!include` directives to your YAML files.
 * `Data.Yaml.Builder` and `Data.Yaml.Parser` allow more fine-grained control of parsing an rendering, as opposed to just using the aeson typeclass and datatype system for parsing and rendering.
 * `Data.Yaml.Aeson` is currently a re-export of `Data.Yaml` to explicitly choose to use the aeson-compatible API.
+
+### Executables
+
+Converters `json2yaml` and `yaml2json` can be built by disabling flag `no-exe`, e.g., one of:
+```
+cabal install yaml -f-no-exe
+stack install yaml --flag yaml:-no-exe
+```
diff --git a/exe/Common.hs b/exe/Common.hs
new file mode 100644
--- /dev/null
+++ b/exe/Common.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE LambdaCase #-}
+
+-- | Code shared between @yaml2json@ and @json2yaml@.
+
+module Common where
+
+import Data.List            ( intercalate )
+#if !MIN_VERSION_base(4,11,0)
+import Data.Semigroup       ( Semigroup(..) )
+#endif
+import Data.Version         ( Version(versionBranch) )
+
+import Options.Applicative  ( Parser, help, hidden, infoOption, long, short )
+
+import qualified Paths_yaml as Paths
+
+-- * Version info and option
+---------------------------------------------------------------------------
+
+-- | Homepage for @yaml2json@ and @json2yaml@.
+
+homepage :: String
+homepage = "https://github.com/snoyberg/yaml/"
+
+-- | Version in @x.y.z@ format.
+
+version :: String
+version = intercalate "." $ map show $ versionBranch Paths.version
+
+-- | The version header including given program name and 'homepage'.
+
+versionText :: String -> String
+versionText self = unwords
+  [ self
+  , "version"
+  , version
+  , homepage  -- concat [ "<", homepage, ">" ]
+  ]
+
+-- | Option @--numeric-version@.
+
+numericVersionOption :: Parser (a -> a)
+numericVersionOption =
+  infoOption version
+    $  long "numeric-version"
+    <> hidden
+    <> help "Show just version number."
+
+-- | Option @--version@.
+
+versionOption :: String -> Parser (a -> a)
+versionOption self =
+  infoOption (versionText self)
+    $  long "version"
+    <> short 'V'
+    <> hidden
+    <> help "Show version info."
+
+-- * Misc
+---------------------------------------------------------------------------
+
+-- | @Just@ unless argument is @"-"@ (denoting @stdin@ or @stdout@).
+
+dashToNothing :: FilePath -> Maybe FilePath
+dashToNothing = \case
+  "-"  -> Nothing
+  file -> Just file
diff --git a/exe/json2yaml.hs b/exe/json2yaml.hs
--- a/exe/json2yaml.hs
+++ b/exe/json2yaml.hs
@@ -1,22 +1,106 @@
-import qualified Data.Aeson as J
-import qualified Data.ByteString as S
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE NamedFieldPuns #-}
+
+import qualified Data.Aeson           as J
+import qualified Data.ByteString      as S
 import qualified Data.ByteString.Lazy as L
-import System.Environment (getArgs)
+import qualified Data.Yaml            as Y
+#if !MIN_VERSION_base(4,11,0)
+import Data.Semigroup ( Semigroup(..) )
+#endif
 
-import qualified Data.Yaml as Y
+import Options.Applicative
+  ( Parser, (<|>)
+  , action, execParser, footerDoc, headerDoc, help, helper, hidden, info
+  , long, metavar, short, strArgument, strOption, value
+  )
+import Options.Applicative.Help.Pretty
+  ( vcat, pretty )
 
+import System.Exit    ( die )
+
+import Common         ( dashToNothing, versionText, versionOption, numericVersionOption )
+
+data Options = Options
+  { optInput  :: Maybe FilePath
+      -- ^ 'Nothing' means @stdin@.
+  , optOutput :: Maybe FilePath
+      -- ^ 'Nothing' means @stdout@.
+  }
+
+-- | Name of the executable.
+
+self :: String
+self = "json2yaml"
+
+-- | Parse options; handle parse errors, @--help@, @--version@.
+
+options :: IO Options
+options =
+  execParser $
+    info (helper <*> versionOption self <*> numericVersionOption <*> programOptions)
+      $  headerDoc hdoc
+      <> footerDoc fdoc
+
+  where
+  hdoc = Just $ vcat $ map pretty
+    [ versionText self
+    , "Convert JSON to YAML."
+    ]
+  fdoc = Just $ pretty $ concat
+    [ "The old call pattern '"
+    , self
+    , " IN OUT' is also accepted, but deprecated."
+    ]
+
+  programOptions :: Parser Options
+  programOptions = Options <$> oInput <*> oOutput
+
+  oInput :: Parser (Maybe FilePath)
+  oInput = dashToNothing <$> do
+    strArgument
+      $  metavar "IN"
+      <> value "-"
+      <> action "file"
+      <> help "The input file containing the JSON document; use '-' for stdin (default)."
+
+  oOutput :: Parser (Maybe FilePath)
+  oOutput = dashToNothing <$> do
+    oOutputExplicit <|> oOutputImplicit
+
+  oOutputExplicit :: Parser FilePath
+  oOutputExplicit =
+    strOption
+      $  long "output"
+      <> short 'o'
+      <> metavar "OUT"
+      <> action "file"
+      -- <> help "The file to hold the produced YAML document; use '-' for stdout (default)."
+
+  oOutputImplicit :: Parser FilePath
+  oOutputImplicit =
+    strArgument
+      $  metavar "OUT"
+      <> value "-"
+      <> action "file"
+      <> hidden
+      <> help "The file to hold the produced YAML document; use '-' for stdout (default)."
+
+-- | Exit with 'self'-stamped error message.
+
+abort :: String -> IO a
+abort msg = die $ concat [ self, ": ", msg ]
+
 main :: IO ()
 main = do
-    args <- getArgs
-    (input, output) <- case args ++ replicate (2 - length args) "-" of
-        [i, o] -> return (i, o)
-        _ -> fail "Usage: json2yaml [in] [out]"
-    mval <- fmap J.decode $
-        case input of
-            "-" -> L.getContents
-            _ -> L.readFile input
-    case mval of
-        Nothing -> error "Invalid input JSON"
-        Just val -> case output of
-            "-" -> S.putStr $ Y.encode (val :: Y.Value)
-            _ -> Y.encodeFile output val
+  Options{ optInput, optOutput } <- options
+
+  -- Read JSON.
+  mval <- J.decode <$> maybe L.getContents L.readFile optInput
+
+  -- Write YAML.
+  case mval of
+    Nothing  -> abort "Invalid input JSON"
+    Just val -> case optOutput of
+      Nothing -> S.putStr $ Y.encode (val :: Y.Value)
+      Just f  -> Y.encodeFile f val
diff --git a/exe/yaml2json.hs b/exe/yaml2json.hs
--- a/exe/yaml2json.hs
+++ b/exe/yaml2json.hs
@@ -1,31 +1,87 @@
-import Prelude hiding (putStr, getContents)
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
 
-import Data.Aeson (encode, Value)
-import Data.ByteString (getContents)
-import Data.ByteString.Lazy (putStr)
-import System.Environment (getArgs)
-import System.Exit
-import System.IO (stderr, hPutStrLn)
+import Data.Aeson           (encode, Value)
+import qualified Data.ByteString      as S (getContents)
+import qualified Data.ByteString.Lazy as L (putStr, writeFile)
+#if !MIN_VERSION_base(4,11,0)
+import Data.Semigroup       (Semigroup(..))
+#endif
+import Data.Yaml            (decodeFileEither, decodeEither')
+import System.Exit          (die)
 
-import Data.Yaml (decodeFileEither, decodeEither')
+import Options.Applicative
+  ( Parser
+  , action, execParser, headerDoc, help, helper, info
+  , long, metavar, short, strArgument, strOption, value
+  )
+import Options.Applicative.Help.Pretty
+  ( vcat, pretty )
 
-helpMessage :: IO ()
-helpMessage = putStrLn "Usage: yaml2json FILE\n\nuse '-' as FILE to indicate stdin" >> exitFailure
+import Common
+  ( versionText, versionOption, numericVersionOption, dashToNothing )
 
-showJSON :: Show a => Either a Value -> IO b
-showJSON ejson =
-    case ejson of
-       Left err -> hPutStrLn stderr (show err) >> exitFailure
-       Right res -> putStr (encode (res :: Value)) >> exitSuccess
+data Options = Options
+  { optInput :: Maybe FilePath
+      -- ^ 'Nothing' means @stdin@.
+  , optOutput :: Maybe FilePath
+      -- ^ 'Nothing' means @stdout@.
+  }
 
+-- | Name of the executable.
+
+self :: String
+self = "yaml2json"
+
+-- | Parse options; handle parse errors, @--help@, @--version@.
+
+options :: IO Options
+options =
+  execParser $
+    info (helper <*> versionOption self <*> numericVersionOption <*> programOptions)
+         (headerDoc hdoc)
+
+  where
+  hdoc = Just $ vcat $ map pretty
+    [ versionText self
+    , "Convert YAML to JSON."
+    ]
+
+  programOptions = Options <$> oInput <*> oOutput
+
+  oInput :: Parser (Maybe FilePath)
+  oInput = dashToNothing <$> do
+    strArgument
+      $  metavar "IN"
+      <> value "-"
+      <> action "file"
+      <> help "The input file containing the YAML document; use '-' for stdin (default)."
+
+  oOutput :: Parser (Maybe FilePath)
+  oOutput = dashToNothing <$> do
+    strOption
+      $  long "output"
+      <> short 'o'
+      <> value "-"
+      <> metavar "OUT"
+      <> action "file"
+      <> help "The file to hold the produced JSON document; use '-' for stdout (default)."
+
+
 main :: IO ()
 main = do
-    args <- getArgs
-    case args of
-       -- strict getContents will read in all of stdin at once
-       (["-h"]) -> helpMessage
-       (["--help"]) -> helpMessage
-       (["-"]) -> getContents >>= showJSON . decodeEither'
-       ([f])   -> decodeFileEither f >>= showJSON
-       _ -> helpMessage
+  Options{ optInput, optOutput } <- options
 
+  -- Input YAML.
+  result <- case optInput of
+    -- strict getContents will read in all of stdin at once
+    Nothing -> decodeEither' <$> S.getContents
+    Just f  -> decodeFileEither f
+
+  -- Output JSON.
+  case result of
+    Left  err -> die $ show err
+    Right val -> do
+      let json = encode (val :: Value)
+      maybe L.putStr L.writeFile optOutput json
diff --git a/src/Data/Yaml/Config.hs b/src/Data/Yaml/Config.hs
--- a/src/Data/Yaml/Config.hs
+++ b/src/Data/Yaml/Config.hs
@@ -33,7 +33,13 @@
 import Data.Semigroup
 import Data.List.NonEmpty (nonEmpty)
 import Data.Aeson
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as H
+import Data.Aeson.KeyMap (KeyMap)
+#else
 import qualified Data.HashMap.Strict as H
+#endif
 import Data.Text (Text, pack)
 import System.Environment (getArgs, getEnvironment)
 import Control.Arrow ((***))
@@ -45,6 +51,16 @@
 import Data.Maybe (fromMaybe)
 import qualified Data.Text as T
 
+#if MIN_VERSION_aeson(2,0,0)
+fromText :: T.Text -> Key
+fromText = K.fromText
+#else
+fromText :: T.Text -> T.Text
+fromText = id
+
+type KeyMap a = H.HashMap T.Text a
+#endif
+
 newtype MergedValue = MergedValue { getMergedValue :: Value }
 
 instance Semigroup MergedValue where
@@ -64,7 +80,7 @@
 --
 -- @since 0.8.16
 applyEnvValue :: Bool -- ^ require an environment variable to be present?
-              -> H.HashMap Text Text -> Value -> Value
+              -> KeyMap Text -> Value -> Value
 applyEnvValue requireEnv' env =
     goV
   where
@@ -74,7 +90,7 @@
         t2 <- T.stripPrefix "_env:" t1
         let (name, t3) = T.break (== ':') t2
             mdef = fmap parseValue $ T.stripPrefix ":" t3
-        Just $ case H.lookup name env of
+        Just $ case H.lookup (fromText name) env of
             Just val ->
                 -- If the default value parses as a String, we treat the
                 -- environment variable as a raw value and do not parse it.
@@ -101,8 +117,8 @@
 -- | Get the actual environment as a @HashMap@ from @Text@ to @Text@.
 --
 -- @since 0.8.16
-getCurrentEnv :: IO (H.HashMap Text Text)
-getCurrentEnv = fmap (H.fromList . map (pack *** pack)) getEnvironment
+getCurrentEnv :: IO (KeyMap Text)
+getCurrentEnv = fmap (H.fromList . map (fromText . pack *** pack)) getEnvironment
 
 -- | A convenience wrapper around 'applyEnvValue' and 'getCurrentEnv'
 --
@@ -118,8 +134,8 @@
 data EnvUsage = IgnoreEnv
               | UseEnv
               | RequireEnv
-              | UseCustomEnv (H.HashMap Text Text)
-              | RequireCustomEnv (H.HashMap Text Text)
+              | UseCustomEnv (KeyMap Text)
+              | RequireCustomEnv (KeyMap Text)
 
 -- | Do not use any environment variables, instead relying on defaults values
 -- in the config file.
@@ -146,14 +162,14 @@
 -- @HashMap@ as the environment.
 --
 -- @since 0.8.16
-useCustomEnv :: H.HashMap Text Text -> EnvUsage
+useCustomEnv :: KeyMap Text -> EnvUsage
 useCustomEnv = UseCustomEnv
 
 -- | Same as 'requireEnv', but instead of the actual environment, use the
 -- provided @HashMap@ as the environment.
 --
 -- @since 0.8.16
-requireCustomEnv :: H.HashMap Text Text -> EnvUsage
+requireCustomEnv :: KeyMap Text -> EnvUsage
 requireCustomEnv = RequireCustomEnv
 
 -- | Load the settings from the following three sources:
diff --git a/src/Data/Yaml/Internal.hs b/src/Data/Yaml/Internal.hs
--- a/src/Data/Yaml/Internal.hs
+++ b/src/Data/Yaml/Internal.hs
@@ -9,12 +9,14 @@
     , prettyPrintParseException
     , Warning(..)
     , parse
+    , Parse
     , decodeHelper
     , decodeHelper_
     , decodeAllHelper
     , decodeAllHelper_
     , textToScientific
     , stringScalar
+    , StringStyle
     , defaultStringStyle
     , isSpecialString
     , specialStrings
@@ -32,9 +34,24 @@
 import Control.Monad.Trans.Resource (ResourceT, runResourceT)
 import Control.Monad.State.Strict
 import Control.Monad.Reader
+#if MIN_VERSION_aeson(2,1,2)
+import Data.Aeson hiding (AesonException)
+#else
 import Data.Aeson
-import Data.Aeson.Internal (JSONPath, JSONPathElement(..), formatError)
+#endif
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as M
+import Data.Aeson.KeyMap (KeyMap)
+#else
+import qualified Data.HashMap.Strict as M
+#endif
+#if MIN_VERSION_aeson(2,2,0)
+import Data.Aeson.Types hiding (AesonException, parse)
+#else
 import Data.Aeson.Types hiding (parse)
+import Data.Aeson.Internal (JSONPath, JSONPathElement(..), formatError)
+#endif
 import qualified Data.Attoparsec.Text as Atto
 import Data.Bits (shiftL, (.|.))
 import Data.ByteString (ByteString)
@@ -42,17 +59,16 @@
 import qualified Data.ByteString.Lazy as BL
 import Data.ByteString.Builder.Scientific (scientificBuilder)
 import Data.Char (toUpper, ord)
-import Data.List
+import qualified Data.List as List
 import Data.Conduit ((.|), ConduitM, runConduit)
 import qualified Data.Conduit.List as CL
-import qualified Data.HashMap.Strict as M
 import qualified Data.HashSet as HashSet
 import           Data.Map (Map)
 import qualified Data.Map as Map
 import           Data.Set (Set)
 import qualified Data.Set as Set
 import Data.Scientific (Scientific, base10Exponent, coefficient)
-import Data.Text (Text, pack)
+import Data.Text (Text)
 import qualified Data.Text as T
 import Data.Text.Encoding (decodeUtf8With, encodeUtf8)
 import Data.Text.Encoding.Error (lenientDecode)
@@ -63,6 +79,23 @@
 import qualified Text.Libyaml as Y
 import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)
 
+#if MIN_VERSION_aeson(2,0,0)
+fromText :: T.Text -> K.Key
+fromText = K.fromText
+
+toText :: K.Key -> T.Text
+toText = K.toText
+#else
+fromText :: T.Text -> T.Text
+fromText = id
+
+toText :: Key -> T.Text
+toText = id
+
+type KeyMap a = M.HashMap Text a
+type Key = Text
+#endif
+
 data ParseException = NonScalarKey
                     | UnknownAlias { _anchorName :: Y.AnchorName }
                     | UnexpectedEvent { _received :: Maybe Event
@@ -255,9 +288,9 @@
             o <- local (Index n :) parseO
             parseS (succ n) a $ front . (:) o
 
-parseM :: Set Text
+parseM :: Set Key
        -> Y.Anchor
-       -> M.HashMap Text Value
+       -> KeyMap Value
        -> ReaderT JSONPath (ConduitM Event o Parse) Value
 parseM mergedKeys a front = do
     me <- lift CL.head
@@ -268,12 +301,12 @@
             return res
         _ -> do
             s <- case me of
-                    Just (EventScalar v tag style a') -> parseScalar v a' style tag
+                    Just (EventScalar v tag style a') -> fromText <$> parseScalar v a' style tag
                     Just (EventAlias an) -> do
                         m <- lookupAnchor an
                         case m of
                             Nothing -> liftIO $ throwIO $ UnknownAlias an
-                            Just (String t) -> return t
+                            Just (String t) -> return $ fromText t
                             Just v -> liftIO $ throwIO $ NonStringKeyAlias an v
                     _ -> do
                         path <- ask
@@ -286,17 +319,17 @@
                           path <- reverse <$> ask
                           addWarning (DuplicateKey path)
                       return (Set.delete s mergedKeys, M.insert s o front)
-              if s == pack "<<"
+              if s == "<<"
                          then case o of
                                   Object l  -> return (merge l)
-                                  Array l -> return $ merge $ foldl' mergeObjects M.empty $ V.toList l
+                                  Array l -> return $ merge $ List.foldl' mergeObjects M.empty $ V.toList l
                                   _          -> al
                          else al
             parseM mergedKeys' a al'
     where mergeObjects al (Object om) = M.union al om
           mergeObjects al _           = al
 
-          merge xs = (Set.fromList (M.keys xs \\ M.keys front), M.union front xs)
+          merge xs = (Set.fromList (M.keys xs List.\\ M.keys front), M.union front xs)
 
 parseSrc :: ReaderT JSONPath (ConduitM Event Void Parse) val
          -> ConduitM () Event Parse ()
@@ -417,7 +450,7 @@
       : foldr pairToEvents (EventMappingEnd : rest) (M.toList o)
       where
         pairToEvents :: Pair -> [Y.Event] -> [Y.Event]
-        pairToEvents (k, v) = objToEvents' (String k) . objToEvents' v
+        pairToEvents (k, v) = objToEvents' (String $ toText k) . objToEvents' v
 
     objToEvents' (String s) rest = stringScalar stringStyle Nothing s : rest
 
diff --git a/src/Data/Yaml/Pretty.hs b/src/Data/Yaml/Pretty.hs
--- a/src/Data/Yaml/Pretty.hs
+++ b/src/Data/Yaml/Pretty.hs
@@ -18,10 +18,16 @@
 #if !MIN_VERSION_base(4,8,0)
 import Control.Applicative ((<$>))
 #endif
+import Data.Bifunctor (first)
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as HM
+#else
+import qualified Data.HashMap.Strict as HM
+#endif
 import Data.Aeson.Types
 import Data.ByteString (ByteString)
 import Data.Function (on)
-import qualified Data.HashMap.Strict as HM
 import Data.List (sortBy)
 #if !MIN_VERSION_base(4,8,0)
 import Data.Monoid
@@ -31,6 +37,16 @@
 
 import Data.Yaml.Builder
 
+#if MIN_VERSION_aeson(2,0,0)
+toText :: Key -> Text
+toText = K.toText
+#else
+toText :: Key -> Text
+toText = id
+
+type Key = Text
+#endif
+
 -- |
 -- @since 0.8.13
 data Config = Config
@@ -72,7 +88,7 @@
                             select
                               | confDropNull cfg = HM.filter (/= Null)
                               | otherwise        = id
-                        in mapping (sort $ HM.toList $ HM.map go $ select o)
+                        in mapping (sort $ fmap (first toText) $ HM.toList $ HM.map go $ select o)
         go (Array a)  = array (go <$> V.toList a)
         go Null       = null
         go (String s) = string s
diff --git a/test/Data/Yaml/IncludeSpec.hs b/test/Data/Yaml/IncludeSpec.hs
--- a/test/Data/Yaml/IncludeSpec.hs
+++ b/test/Data/Yaml/IncludeSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE OverloadedStrings #-}
 module Data.Yaml.IncludeSpec (main, spec) where
@@ -6,7 +7,11 @@
 import           Data.List (isPrefixOf)
 import qualified Data.ByteString.Lazy as LB
 import           Data.Aeson
+#if MIN_VERSION_aeson(2,2,0)
+import           Data.Aeson.Types (JSONPathElement(..))
+#else
 import           Data.Aeson.Internal (JSONPathElement(..))
+#endif
 import           Data.Yaml (ParseException(InvalidYaml))
 import           Data.Yaml.Include
 import           Data.Yaml.Internal
diff --git a/test/Data/YamlSpec.hs b/test/Data/YamlSpec.hs
--- a/test/Data/YamlSpec.hs
+++ b/test/Data/YamlSpec.hs
@@ -32,10 +32,18 @@
 import qualified Data.Yaml.Pretty as Pretty
 import Data.Yaml (object, array, (.=))
 import Data.Maybe
+import qualified Data.HashMap.Strict as HM
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.KeyMap as M
+import qualified Data.Aeson.Key as K
+import Data.Aeson.KeyMap (KeyMap)
+#else
 import qualified Data.HashMap.Strict as M
+#endif
 import qualified Data.Text as T
 import Data.Aeson.TH
 import Data.Scientific (Scientific)
+import Data.String (fromString)
 import Data.Text (Text)
 import Data.Text.Encoding (decodeUtf8, encodeUtf8)
 import Data.Vector (Vector)
@@ -45,6 +53,23 @@
 import System.IO (hClose)
 import System.IO.Temp (withSystemTempFile)
 
+#if MIN_VERSION_aeson(2,0,0)
+fromText :: T.Text -> K.Key
+fromText = K.fromText
+
+toText :: K.Key -> T.Text
+toText = K.toText
+#else
+fromText :: T.Text -> T.Text
+fromText = id
+
+toText :: Key -> T.Text
+toText = id
+
+type KeyMap a = M.HashMap Text a
+type Key = Text
+#endif
+
 data TestJSON = TestJSON
               { string :: Text
               , number :: Int
@@ -179,7 +204,7 @@
 
     describe "special keys" $ do
         let tester key = it (T.unpack key) $
-                let value = object [key .= True]
+                let value = object [fromText key .= True]
                  in D.encode value `shouldDecode` value
         mapM_ tester specialStrings
 
@@ -551,7 +576,7 @@
 mkStrScalar = D.String . T.pack
 
 mappingKey :: D.Value-> String -> D.Value
-mappingKey (D.Object m) k = (fromJust . M.lookup (T.pack k) $ m)
+mappingKey (D.Object m) k = (fromJust . M.lookup (fromText $ T.pack k) $ m)
 mappingKey _ _ = error "expected Object"
 
 sample :: D.Value
@@ -658,7 +683,9 @@
 caseSimpleMappingAlias :: Assertion
 caseSimpleMappingAlias =
     "map: &anch\n  key1: foo\n  key2: baz\nmap2: *anch" `shouldDecode`
-    object [(T.pack "map", object [("key1", mkScalar "foo"), ("key2", (mkScalar "baz"))]), (T.pack "map2", object [("key1", (mkScalar "foo")), ("key2", mkScalar "baz")])]
+    object [(packStr "map", object [("key1", mkScalar "foo"), ("key2", (mkScalar "baz"))]), (packStr "map2", object [("key1", (mkScalar "foo")), ("key2", mkScalar "baz")])]
+  where
+    packStr = fromText . T.pack
 
 caseMappingAliasBeforeAnchor :: Assertion
 caseMappingAliasBeforeAnchor =
@@ -811,10 +838,10 @@
     res <- D.decodeFileEither fp
     either (Left . show) Right res `shouldBe` Right val
 
-caseSpecialKeys :: (HashMap Text () -> B8.ByteString) -> Assertion
+caseSpecialKeys :: (KeyMap () -> B8.ByteString) -> Assertion
 caseSpecialKeys encoder = do
       let keys = T.words "true false NO YES 1.2 1e5 null"
-          bs = encoder $ M.fromList $ map (, ()) keys
+          bs = encoder $ M.fromList $ map (, ()) $ fmap fromText keys
           text = decodeUtf8 bs
       forM_ keys $ \key -> do
         let quoted = T.concat ["'", key, "'"]
diff --git a/yaml.cabal b/yaml.cabal
--- a/yaml.cabal
+++ b/yaml.cabal
@@ -1,13 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.33.0.
+-- This file has been generated from package.yaml by hpack version 0.35.1.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 55c85c8d4d3074a558a82e30a2592ecff9db2e6f1571547c73d26ba44bfc1c20
 
 name:           yaml
-version:        0.11.5.0
+version:        0.11.11.2
 synopsis:       Support for parsing and rendering YAML documents.
 description:    README and API documentation are available at <https://www.stackage.org/package/yaml>
 category:       Data
@@ -62,8 +60,9 @@
       Paths_yaml
   hs-source-dirs:
       src
-  other-extensions: LambdaCase
-  ghc-options: -Wall
+  other-extensions:
+      LambdaCase
+  ghc-options: -Wall -Wcompat
   build-depends:
       aeson >=0.11
     , attoparsec >=0.11.3.0
@@ -73,18 +72,15 @@
     , containers
     , directory
     , filepath
-    , libyaml >=0.1 && <0.2
+    , libyaml ==0.1.*
     , mtl
-    , resourcet >=0.3 && <1.3
+    , resourcet >=0.3 && <1.4
     , scientific >=0.3
     , template-haskell
     , text
     , transformers >=0.1
     , unordered-containers
     , vector
-  if !impl(ghc >= 8.0)
-    build-depends:
-        semigroups
   default-language: Haskell2010
 
 executable examples
@@ -95,7 +91,7 @@
       Paths_yaml
   hs-source-dirs:
       examples
-  ghc-options: -Wall
+  ghc-options: -Wall -Wcompat
   build-depends:
       aeson >=0.11
     , attoparsec >=0.11.3.0
@@ -105,32 +101,31 @@
     , containers
     , directory
     , filepath
-    , libyaml >=0.1 && <0.2
+    , libyaml ==0.1.*
     , mtl
-    , resourcet >=0.3 && <1.3
+    , resourcet >=0.3 && <1.4
     , scientific >=0.3
     , template-haskell
     , text
     , transformers >=0.1
     , unordered-containers
     , vector
-  if !impl(ghc >= 8.0)
-    build-depends:
-        semigroups
+  default-language: Haskell2010
   if flag(no-examples)
     buildable: False
   else
     build-depends:
         raw-strings-qq
       , yaml
-  default-language: Haskell2010
 
 executable json2yaml
   main-is: json2yaml.hs
   other-modules:
+      Common
       Paths_yaml
   hs-source-dirs:
       exe
+  ghc-options: -Wall -Wcompat
   build-depends:
       aeson >=0.11
     , attoparsec >=0.11.3.0
@@ -140,9 +135,10 @@
     , containers
     , directory
     , filepath
-    , libyaml >=0.1 && <0.2
+    , libyaml ==0.1.*
     , mtl
-    , resourcet >=0.3 && <1.3
+    , optparse-applicative
+    , resourcet >=0.3 && <1.4
     , scientific >=0.3
     , template-haskell
     , text
@@ -150,19 +146,22 @@
     , unordered-containers
     , vector
     , yaml
-  if !impl(ghc >= 8.0)
-    build-depends:
-        semigroups
+  default-language: Haskell2010
   if flag(no-exe)
     buildable: False
-  default-language: Haskell2010
 
 executable yaml2json
   main-is: yaml2json.hs
   other-modules:
+      Common
       Paths_yaml
   hs-source-dirs:
       exe
+  other-extensions:
+      CPP
+      LambdaCase
+      NamedFieldPuns
+  ghc-options: -Wall -Wcompat
   build-depends:
       aeson >=0.11
     , attoparsec >=0.11.3.0
@@ -172,9 +171,10 @@
     , containers
     , directory
     , filepath
-    , libyaml >=0.1 && <0.2
+    , libyaml ==0.1.*
     , mtl
-    , resourcet >=0.3 && <1.3
+    , optparse-applicative
+    , resourcet >=0.3 && <1.4
     , scientific >=0.3
     , template-haskell
     , text
@@ -182,12 +182,9 @@
     , unordered-containers
     , vector
     , yaml
-  if !impl(ghc >= 8.0)
-    build-depends:
-        semigroups
+  default-language: Haskell2010
   if flag(no-exe)
     buildable: False
-  default-language: Haskell2010
 
 test-suite spec
   type: exitcode-stdio-1.0
@@ -199,7 +196,7 @@
       Paths_yaml
   hs-source-dirs:
       test
-  ghc-options: -Wall "-with-rtsopts=-K1K"
+  ghc-options: -Wall -Wcompat -with-rtsopts=-K1K
   cpp-options: -DTEST
   build-depends:
       HUnit
@@ -213,11 +210,11 @@
     , directory
     , filepath
     , hspec >=1.3
-    , libyaml >=0.1 && <0.2
+    , libyaml ==0.1.*
     , mockery
     , mtl
     , raw-strings-qq
-    , resourcet >=0.3 && <1.3
+    , resourcet >=0.3 && <1.4
     , scientific >=0.3
     , template-haskell
     , temporary
@@ -226,7 +223,4 @@
     , unordered-containers
     , vector
     , yaml
-  if !impl(ghc >= 8.0)
-    build-depends:
-        semigroups
   default-language: Haskell2010
