diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,12 @@
+## 1.5
+
+* Support GHC >= 9.6
+* Use `buildVariantExtractor` in `Codec.Winery.Query.con`
+* Require mtl >= 2.3
+* Added an instance for `Solo`
+* Added `deserialiseOnly`
+* `extractConstructor`, `extractConstructorBy`, and `extractVoid` are now deprecated
+
 ## 1.4
 
 * Added an instance for `NonEmpty`
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -93,17 +93,9 @@
 `Extractor` parses a schema and returns a function which gives a value back from
 a `Term`.
 
-You can build an extractor using combinators such as `extractField`, `extractConstructor`, etc.
-
-```haskell
-buildExtractor
-  $ ("None", \() -> Nothing)
-  `extractConstructor` ("Some", Just)
-  `extractConstructor` extractVoid
-  :: Extractor (Maybe a)
-```
+You can build an extractor using combinators such as `extractField`.
 
-If you want to customise the extractor, the pair of `gvariantExtractors` and `buildVariantExtractors` is handy.
+If you want to customise an extractor for a variant, the pair of `gvariantExtractors` and `buildVariantExtractors` is handy.
 
 ```haskell
 gvariantExtractors :: (GSerialiseVariant (Rep a), Generic a) => HM.HashMap T.Text (Extractor a)
diff --git a/src/Codec/Winery.hs b/src/Codec/Winery.hs
--- a/src/Codec/Winery.hs
+++ b/src/Codec/Winery.hs
@@ -9,6 +9,7 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE UndecidableInstances #-}
+{-# OPTIONS -Wno-deprecations #-}
 ----------------------------------------------------------------------------
 -- |
 -- Module      :  Codec.Winery
@@ -45,6 +46,7 @@
   , Decoder
   , evalDecoder
   , serialiseOnly
+  , deserialiseOnly
   , getDecoder
   , getDecoderBy
   -- * Decoding combinators
@@ -278,6 +280,11 @@
   dec <- getDecoderBy e sch
   return $ evalDecoder dec bs
 
+-- | Deserialise a 'serialise'd 'B.Bytestring' using the current schema.
+deserialiseOnly :: Serialise a => B.ByteString -> a
+deserialiseOnly bs = evalDecoder decodeCurrent bs
+{-# INLINE deserialiseOnly #-}
+
 -- | Deserialise a file. Throws 'WineryException'
 readFileDeserialise :: Serialise a => FilePath -> IO a
 readFileDeserialise path = B.readFile path >>= either throwIO pure . deserialise
@@ -336,6 +343,7 @@
           t -> k t
       _ -> run (unSubextractor cont) (SVariant schs0)
   s -> throwStrategy $ UnexpectedSchema [] "a variant" s
+{-# DEPRECATED extractConstructorBy "Use buildVariantExtractor instead" #-}
 
 -- | Tries to match on a constructor. If it doesn't match (or constructor
 -- doesn't exist at all), leave it to the successor.
@@ -344,6 +352,7 @@
 extractConstructor :: (Serialise a) => (T.Text, a -> r) -> Subextractor r -> Subextractor r
 extractConstructor (name, f) = extractConstructorBy (extractor, name, f)
 {-# INLINE extractConstructor #-}
+{-# DEPRECATED extractConstructor "Use buildVariantExtractor instead" #-}
 
 -- | No constructors remaining.
 extractVoid :: Typeable r => Subextractor r
@@ -351,6 +360,7 @@
   SVariant schs0
     | V.null schs0 -> return $ throw . InvalidTerm
   s -> throwStrategy $ UnexpectedSchema [] "no constructors" s
+{-# DEPRECATED extractVoid "Use buildVariantExtractor mempty instead" #-}
 
 infixr 1 `extractConstructorBy`
 infixr 1 `extractConstructor`
diff --git a/src/Codec/Winery/Class.hs b/src/Codec/Winery/Class.hs
--- a/src/Codec/Winery/Class.hs
+++ b/src/Codec/Winery/Class.hs
@@ -70,7 +70,8 @@
 import Barbies.TH
 import Control.Applicative
 import Control.Exception
-import Control.Monad.Reader
+import Control.Monad
+import Control.Monad.Fix
 import qualified Data.ByteString as B
 import qualified Data.ByteString.FastBuilder as BB
 import qualified Data.ByteString.Lazy as BL
@@ -110,6 +111,7 @@
 import Prettyprinter hiding ((<>), SText, SChar)
 import Data.Time.Clock
 import Data.Time.Clock.POSIX
+import Data.Tuple
 import Data.Typeable
 import Data.Void (Void)
 import Unsafe.Coerce
@@ -117,6 +119,9 @@
 import GHC.Natural
 import GHC.Generics
 import GHC.TypeLits
+#if __GLASGOW_HASKELL__ >= 906
+  hiding (SChar)
+#endif
 
 -- | Serialisable datatype
 --
@@ -1105,3 +1110,13 @@
 bextractors :: forall b. (ConstraintsB b, AllB Serialise b, FieldNamesB b) => b Subextractor
 bextractors = bmapC @Serialise (extractField . getConst) bfieldNames
 {-# INLINABLE bextractors #-}
+
+#if MIN_VERSION_base(4,15,0)
+instance Serialise a => Serialise (Solo a) where
+  schemaGen = gschemaGenProduct
+  toBuilder = gtoBuilderProduct
+  extractor = gextractorProduct
+  decodeCurrent = gdecodeCurrentProduct
+  {-# INLINE toBuilder #-}
+  {-# INLINE decodeCurrent #-}
+#endif
diff --git a/src/Codec/Winery/Internal.hs b/src/Codec/Winery/Internal.hs
--- a/src/Codec/Winery/Internal.hs
+++ b/src/Codec/Winery/Internal.hs
@@ -53,6 +53,7 @@
 import qualified Data.ByteString.Builder.Prim.Internal as BPI
 import Data.Bits
 import Data.String
+import Data.Tuple
 import qualified Data.Vector.Unboxed as U
 import qualified Data.Vector as V
 import Data.Word
diff --git a/src/Codec/Winery/Query.hs b/src/Codec/Winery/Query.hs
--- a/src/Codec/Winery/Query.hs
+++ b/src/Codec/Winery/Query.hs
@@ -28,6 +28,7 @@
 import Data.Typeable
 import qualified Data.Text as T
 import qualified Data.Vector as V
+import qualified Data.HashMap.Strict as HM
 
 -- | Query is a transformation between 'Extractor's.
 -- Like jq, this returns a list of values.
@@ -71,7 +72,7 @@
 
 -- | Takes a variant and returns a value when the constructor matches.
 con :: Typeable a => T.Text -> Query a a
-con name = Query $ \d -> buildExtractor $ extractConstructorBy (d, name, id) (pure [])
+con name = Query $ \d -> buildVariantExtractor $ HM.singleton name d
 
 -- | Propagate values if the supplied 'Query' doesn't return False.
 select :: Query a Bool -> Query a a
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -174,11 +174,11 @@
 
 instance Serialise Food where
   bundleSerialise = bundleVia WineryVariant
-  extractor = buildExtractor
-    $ ("Rice", \() -> Rice)
-    `extractConstructor` ("Ramen", Ramen)
-    `extractConstructor` ("Pasta", uncurry Pasta)
-    `extractConstructor` extractVoid
+  extractor = buildVariantExtractor $ HM.fromList
+    [ ("Rice", pure Rice)
+    , ("Ramen", Ramen . getSingleField <$> extractor)
+    , ("Pasta", uncurry Pasta <$> extractor)
+    ]
 
 prop_Food = testSerialise @Food
 
diff --git a/winery.cabal b/winery.cabal
--- a/winery.cabal
+++ b/winery.cabal
@@ -1,6 +1,6 @@
 cabal-version:  2.0
 name:           winery
-version:        1.4
+version:        1.5
 synopsis:       A compact, well-typed seralisation format for Haskell values
 description:
   <<https://i.imgur.com/lTosHnE.png>>
@@ -50,7 +50,7 @@
     , hashable
     , HUnit
     , megaparsec >=7.0.0
-    , mtl
+    , mtl >= 2.3
     , prettyprinter ^>= 1.7
     , prettyprinter-ansi-terminal
     , scientific
