diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 3.5.0
+
+- Support for `aeson-2.0`.
+- Removed the `kanji` executable.
+
 ## 3.4.1
 
 - Drop support for GHC versions that haven't implemented the Semigroup-Monoid
diff --git a/kanji.cabal b/kanji.cabal
--- a/kanji.cabal
+++ b/kanji.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               kanji
-version:            3.4.1
+version:            3.5.0
 synopsis:
   Perform 漢字検定 (Japan Kanji Aptitude Test) level analysis on Japanese Kanji
 
@@ -11,7 +11,7 @@
 homepage:           https://github.com/fosskers/kanji
 author:             Colin Woodbury
 maintainer:         colin@fosskers.ca
-copyright:          2011 - 2020 Colin Woodbury
+copyright:          2011 - 2022 Colin Woodbury
 license:            BSD-3-Clause
 license-file:       LICENSE
 build-type:         Simple
@@ -22,7 +22,7 @@
 common commons
   default-language: Haskell2010
   build-depends:
-    , aeson       >=1.2
+    , aeson       ^>= 2.0
     , base        >=4.7   && <5
     , containers  >=0.5.8
     , text
@@ -43,18 +43,18 @@
     Data.Kanji.Levels
     Data.Kanji.Types
 
-executable kanji
-  import:         commons
-  main-is:        nanq.hs
-  hs-source-dirs: nanq
-  ghc-options:    -O2
-  build-depends:
-    , aeson-pretty
-    , kanji
-    , microlens             >=0.4
-    , microlens-aeson       >=2.2
-    , optparse-applicative  >=0.14
-    , transformers
+-- executable kanji
+--   import:         commons
+--   main-is:        nanq.hs
+--   hs-source-dirs: nanq
+--   ghc-options:    -O2
+--   build-depends:
+--     , aeson-pretty
+--     , kanji
+--     , microlens             >=0.4
+--     , microlens-aeson       >=2.2
+--     , optparse-applicative  >=0.14
+--     , transformers
 
 test-suite kanji-test
   import:         commons
diff --git a/lib/Data/Kanji/Types.hs b/lib/Data/Kanji/Types.hs
--- a/lib/Data/Kanji/Types.hs
+++ b/lib/Data/Kanji/Types.hs
@@ -14,14 +14,12 @@
 
 module Data.Kanji.Types where
 
-import           Control.DeepSeq (NFData)
-import           Data.Aeson
-import           Data.Aeson.Encoding (text)
-import           Data.Bool (bool)
-import           Data.Char (isLetter, isNumber, isPunctuation, ord)
-import           Data.Hashable
-import qualified Data.Text as T
-import           GHC.Generics
+import Control.DeepSeq (NFData)
+import Data.Aeson
+import Data.Bool (bool)
+import Data.Char (isLetter, isNumber, isPunctuation, ord)
+import Data.Hashable
+import GHC.Generics
 
 ---
 
@@ -62,12 +60,7 @@
 -- this library.
 data Level = Ten | Nine | Eight | Seven | Six | Five | Four | Three | PreTwo
            | Two | PreOne | One | Unknown
-           deriving (Eq, Ord, Enum, Show, Generic, Hashable, NFData, ToJSON, FromJSON)
-
-instance ToJSONKey Level where
-  toJSONKey = ToJSONKeyText f g
-    where f = T.pack . show
-          g = text . T.pack . show
+           deriving (Eq, Ord, Enum, Show, Generic, Hashable, NFData, ToJSON, FromJSON, ToJSONKey)
 
 -- | Legal Kanji appear between UTF-8 characters 19968 and 40959.
 isKanji :: Char -> Bool
@@ -90,7 +83,7 @@
 -- Japanese "full-width" numbers and letters will be counted as `Numeral`
 -- and `RomanLetter` respectively, alongside their usual ASCII forms.
 data CharCat = Hanzi | Hiragana | Katakana | Numeral | RomanLetter | Punctuation | Other
-  deriving (Eq, Ord, Show, Generic, Hashable, NFData, ToJSON, FromJSON)
+  deriving (Eq, Ord, Show, Generic, Hashable, NFData, ToJSON, FromJSON, ToJSONKey)
 
 category :: Char -> CharCat
 category c | isKanji c       = Hanzi
@@ -100,8 +93,3 @@
            | isNumber c      = Numeral
            | isPunctuation c = Punctuation
            | otherwise       = Other
-
-instance ToJSONKey CharCat where
-  toJSONKey = ToJSONKeyText f g
-    where f = T.pack . show
-          g = text . T.pack . show
diff --git a/nanq/nanq.hs b/nanq/nanq.hs
deleted file mode 100644
--- a/nanq/nanq.hs
+++ /dev/null
@@ -1,101 +0,0 @@
-{-# LANGUAGE FlexibleContexts  #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TupleSections     #-}
-
-module Main ( main ) where
-
-import           Control.Monad.Trans.Reader
-import           Data.Aeson
-import           Data.Aeson.Encode.Pretty
-import           Data.Kanji
-import qualified Data.Map.Strict as M
-import           Data.Maybe (catMaybes, mapMaybe)
-import qualified Data.Set as S
-import           Data.Text (Text)
-import qualified Data.Text as T
-import qualified Data.Text.IO as TIO
-import           Data.Text.Lazy.Builder (toLazyText)
-import           Data.Text.Lazy.IO as TLIO
-import           Lens.Micro
-import           Lens.Micro.Aeson
-import           Options.Applicative
-
----
-
-data Flags = Flags [Operation] (Either FilePath Text) deriving (Eq)
-
-data Operation = Density | Elementary | Distribution | Splits deriving (Eq)
-
-data Env = Env { _allKs :: [Kanji], _original :: Text } deriving Eq
-
--- | Long, Short, Help
-lsh :: HasName f => String -> Char -> String -> Mod f a
-lsh l s h = long l <> short s <> help h
-
-flags :: Parser Flags
-flags = Flags <$> operations <*> (file <|> japanese)
-  where
-    file = Left <$> strOption (lsh "file" 'f' "Take input from a file")
-    japanese = Right <$> argument str (metavar "JAPANESE")
-
-operations :: Parser [Operation]
-operations = catMaybes <$> ops
-  where
-    ops = traverse optional
-          [ flag' Density $
-            lsh "density" 'd' "Find how much of the input is made of Kanji"
-          , flag' Elementary $
-            lsh "elementary" 'e' "Find density of Kanji learnt in elementary school"
-          , flag' Distribution $
-            lsh "leveldist" 'l' "Find the distribution of Kanji levels"
-          , flag' Splits $
-            lsh "splits" 's' "Show which Level each Kanji belongs to" ]
-
--- | Shortcut for singleton objects
-ob :: ToJSON v => Text -> v -> Value
-ob k v = object [ k .= v ]
-
-splits :: Reader Env Value
-splits = ob "levelSplit" . S.foldl' f mempty . S.fromList <$> asks _allKs
-  where
-    f a k = (\l -> M.insertWith (++) l [k] a) $ level k
-
-distribution :: Reader Env Value
-distribution = ob "distributions" . levelDist <$> asks _allKs
-
-density :: Reader Env Value
-density = do
-  d <- maybe 0 id . M.lookup Hanzi . densities <$> asks _original
-  pure $ ob "density" d
-
-elementaryDensity :: Reader Env Value
-elementaryDensity = ob "elementary" . elementaryDen . levelDist <$> asks _allKs
-
--- | All operations return JSON, to be aggregated into a master Object.
-execOp :: Operation -> Reader Env Value
-execOp Density      = density
-execOp Elementary   = elementaryDensity
-execOp Distribution = distribution
-execOp Splits       = splits
-
-output :: Value -> IO ()
-output = TLIO.putStrLn . toLazyText . encodePrettyToTextBuilder
-
--- | Dispatch on each `Operation` given. Aggregates the resulting JSON.
-work :: (Env, [Operation]) -> Value
-work (e, os) = Object $ vals ^. each . _Object
-  where
-    vals = runReader (traverse execOp os) e
-
-env :: Flags -> IO (Env, [Operation])
-env (Flags os inp) = case inp of
-  Right t -> pure (e t, os)
-  Left  f -> (, os) . e <$> TIO.readFile f
-  where
-    e t = Env (mapMaybe kanji $ T.unpack t) t
-
-main :: IO ()
-main = execParser opts >>= env >>= output . work
-  where
-    opts = info (helper <*> flags)
-           (fullDesc <> header "nanq - Kanji analysis of Japanese text")
