diff --git a/avro.cabal b/avro.cabal
--- a/avro.cabal
+++ b/avro.cabal
@@ -1,13 +1,7 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.31.1.
---
--- see: https://github.com/sol/hpack
---
--- hash: ca29cdadb7a7a9edda357686a62ea84b9122a6bedb7fe04890a5d1fcc8719127
-
 name:           avro
-version:        0.4.1.2
+version:        0.4.2.0
 synopsis:       Avro serialization support for Haskell
 description:    Avro serialization and deserialization support for Haskell
 category:       Data
@@ -88,7 +82,6 @@
   hs-source-dirs:
       src
   other-extensions: OverloadedStrings
-  ghc-options: -O2
   build-depends:
       aeson
     , array
diff --git a/src/Data/Avro/Deriving.hs b/src/Data/Avro/Deriving.hs
--- a/src/Data/Avro/Deriving.hs
+++ b/src/Data/Avro/Deriving.hs
@@ -32,41 +32,41 @@
 )
 where
 
-import           Control.Monad                      (join)
-import           Data.Aeson                         (eitherDecode)
-import qualified Data.Aeson                         as J
-import           Data.Avro                          hiding (decode, encode)
-import           Data.Avro.Schema                   as S
-import qualified Data.Avro.Types                    as AT
-import           Data.ByteString                    (ByteString)
-import qualified Data.ByteString                    as B
-import           Data.Char                          (isAlphaNum)
+import           Control.Monad      (join)
+import           Data.Aeson         (eitherDecode)
+import qualified Data.Aeson         as J
+import           Data.Avro          hiding (decode, encode)
+import           Data.Avro.Schema   as S
+import qualified Data.Avro.Types    as AT
+import           Data.ByteString    (ByteString)
+import qualified Data.ByteString    as B
+import           Data.Char          (isAlphaNum)
 import           Data.Int
-import           Data.List.NonEmpty                 (NonEmpty ((:|)))
-import qualified Data.List.NonEmpty                 as NE
-import           Data.Map                           (Map)
-import           Data.Maybe                         (fromMaybe)
-import           Data.Semigroup                     ((<>))
-import qualified Data.Text                          as Text
+import           Data.List.NonEmpty (NonEmpty ((:|)))
+import qualified Data.List.NonEmpty as NE
+import           Data.Map           (Map)
+import           Data.Maybe         (fromMaybe)
+import           Data.Semigroup     ((<>))
+import qualified Data.Text          as Text
 
 
-import           GHC.Generics                       (Generic)
+import GHC.Generics (Generic)
 
-import           Language.Haskell.TH                as TH hiding (notStrict)
-import           Language.Haskell.TH.Lib            as TH hiding (notStrict)
-import           Language.Haskell.TH.Syntax
+import Language.Haskell.TH        as TH hiding (notStrict)
+import Language.Haskell.TH.Lib    as TH hiding (notStrict)
+import Language.Haskell.TH.Syntax
 
-import           Data.Avro.Deriving.NormSchema
-import           Data.Avro.EitherN
+import Data.Avro.Deriving.NormSchema
+import Data.Avro.EitherN
 
-import qualified Data.ByteString                    as BS
-import qualified Data.ByteString.Lazy               as LBS
-import qualified Data.ByteString.Lazy.Char8         as LBSC8
-import qualified Data.HashMap.Strict                as HM
-import qualified Data.Set                           as S
-import           Data.Text                          (Text)
-import qualified Data.Text                          as T
-import qualified Data.Vector                        as V
+import qualified Data.ByteString            as BS
+import qualified Data.ByteString.Lazy       as LBS
+import qualified Data.ByteString.Lazy.Char8 as LBSC8
+import qualified Data.HashMap.Strict        as HM
+import qualified Data.Set                   as S
+import           Data.Text                  (Text)
+import qualified Data.Text                  as T
+import qualified Data.Vector                as V
 
 import           Data.Avro.Decode.Lazy.FromLazyAvro
 import qualified Data.Avro.Decode.Lazy.LazyValue    as LV
@@ -110,14 +110,14 @@
   { -- | How to build field names for generated data types. The first
     -- argument is the type name to use as a prefix, rendered
     -- according to the 'namespaceBehavior' setting.
-    fieldNameBuilder :: Text -> Field -> T.Text
+    fieldNameBuilder    :: Text -> Field -> T.Text
 
     -- | Determines field representation of generated data types
-  , fieldRepresentation  :: TypeName -> Field -> (FieldStrictness, FieldUnpackedness)
+  , fieldRepresentation :: TypeName -> Field -> (FieldStrictness, FieldUnpackedness)
 
     -- | Controls how we handle namespaces when defining Haskell type
     -- and field names.
-  , namespaceBehavior :: NamespaceBehavior
+  , namespaceBehavior   :: NamespaceBehavior
   } deriving Generic
 
 -- | Default deriving options
@@ -672,10 +672,10 @@
   pure $ NewtypeD [] dn [] Nothing ctor ders
 #else
 genNewtype dn = do
-  [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
+  [ConT eq, ConT sh, ConT gen] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   fldType <- [t|ByteString|]
   let ctor = RecC dn [(mkName ("un" ++ nameBase dn), notStrict, fldType)]
-  pure $ NewtypeD [] dn [] ctor [eq, sh]
+  pure $ NewtypeD [] dn [] ctor [eq, sh, gen]
 #endif
 
 genEnum :: Name -> [Name] -> Q Dec
@@ -689,8 +689,8 @@
   pure $ DataD [] dn [] Nothing ((\n -> NormalC n []) <$> vs) ders
 #else
 genEnum dn vs = do
-  [ConT eq, ConT sh, ConT or, ConT en] <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]
-  pure $ DataD [] dn [] ((\n -> NormalC n []) <$> vs) [eq, sh, or, en]
+  [ConT eq, ConT sh, ConT or, ConT en, ConT gen] <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]
+  pure $ DataD [] dn [] ((\n -> NormalC n []) <$> vs) [eq, sh, or, en, gen]
 #endif
 
 genDataType :: Name -> [VarStrictType] -> Q Dec
@@ -704,8 +704,8 @@
   pure $ DataD [] dn [] Nothing [RecC dn flds] ders
 #else
 genDataType dn flds = do
-  [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
-  pure $ DataD [] dn [] [RecC dn flds] [eq, sh]
+  [ConT eq, ConT sh, ConT gen] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
+  pure $ DataD [] dn [] [RecC dn flds] [eq, sh, gen]
 #endif
 
 notStrict :: Strict
diff --git a/src/Data/Avro/JSON.hs b/src/Data/Avro/JSON.hs
--- a/src/Data/Avro/JSON.hs
+++ b/src/Data/Avro/JSON.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 -- | Avro supports a JSON representation of Avro objects alongside the
 -- Avro binary format. An Avro schema can be used to generate and
@@ -97,8 +98,8 @@
       | otherwise      =
           let
             canonicalize name
-              | Just _ <- Schema.primitiveType name = name
-              | otherwise = Schema.renderFullname $ Schema.parseFullname name
+              | isBuiltIn name = name
+              | otherwise      = Schema.renderFullname $ Schema.parseFullname name
             branch =
               head $ HashMap.keys obj
             names =
@@ -112,6 +113,9 @@
       Avro.Error "Invalid JSON representation for union: has to be a JSON object with exactly one field."
     union _ _ =
       error "Impossible: function given non-union schema."
+
+    isBuiltIn name = name `elem` [ "null", "boolean", "int", "long", "float"
+                                 , "double", "bytes", "string", "array", "map" ]
 
 -- | Convert a 'Aeson.Value' into a type that has an Avro schema. The
 -- schema is used to validate the JSON and will return an 'Error' if
diff --git a/src/Data/Avro/Schema.hs b/src/Data/Avro/Schema.hs
--- a/src/Data/Avro/Schema.hs
+++ b/src/Data/Avro/Schema.hs
@@ -27,7 +27,6 @@
   , validateSchema
   -- * Lower level utilities
   , typeName
-  , primitiveType
   , buildTypeEnvironment
   , extractBindings
 
@@ -51,34 +50,33 @@
 import qualified Control.Monad.Fail         as MF
 import           Control.Monad.State.Strict
 
-import           Data.Aeson                 (FromJSON (..), ToJSON (..), object,
-                                             (.!=), (.:), (.:!), (.:?), (.=))
-import qualified Data.Aeson                 as A
-import           Data.Aeson.Types           (Parser, typeMismatch)
-import qualified Data.Avro.Types            as Ty
-import qualified Data.ByteString            as B
-import qualified Data.ByteString.Base16     as Base16
-import qualified Data.Char                  as Char
-import           Data.Function              (on)
+import           Data.Aeson             (FromJSON (..), ToJSON (..), object, (.!=), (.:), (.:!), (.:?), (.=))
+import qualified Data.Aeson             as A
+import           Data.Aeson.Types       (Parser, typeMismatch)
+import qualified Data.Avro.Types        as Ty
+import qualified Data.ByteString        as B
+import qualified Data.ByteString.Base16 as Base16
+import qualified Data.Char              as Char
+import           Data.Function          (on)
 import           Data.Hashable
-import qualified Data.HashMap.Strict        as HashMap
+import qualified Data.HashMap.Strict    as HashMap
 import           Data.Int
-import qualified Data.IntMap                as IM
-import qualified Data.List                  as L
-import           Data.List.NonEmpty         (NonEmpty (..))
-import qualified Data.List.NonEmpty         as NE
-import           Data.Maybe                 (catMaybes, fromMaybe, isJust)
-import           Data.Monoid                (First (..))
+import qualified Data.IntMap            as IM
+import qualified Data.List              as L
+import           Data.List.NonEmpty     (NonEmpty (..))
+import qualified Data.List.NonEmpty     as NE
+import           Data.Maybe             (catMaybes, fromMaybe, isJust)
+import           Data.Monoid            (First (..))
 import           Data.Semigroup
-import qualified Data.Set                   as S
+import qualified Data.Set               as S
 import           Data.String
-import           Data.Text                  (Text)
-import qualified Data.Text                  as T
-import           Data.Text.Encoding         as T
-import qualified Data.Vector                as V
-import           Prelude                    as P
+import           Data.Text              (Text)
+import qualified Data.Text              as T
+import           Data.Text.Encoding     as T
+import qualified Data.Vector            as V
+import           Prelude                as P
 
-import           Text.Show.Functions        ()
+import Text.Show.Functions ()
 
 -- |An Avro schema is either
 -- * A "JSON object in the form `{"type":"typeName" ...`
@@ -297,27 +295,6 @@
     Union (x:|_) _ -> typeName x
     _              -> renderFullname $ name bt
 
--- | If the given string is the name of a primitive type, return the
--- type, otherwise return 'Nothing'.
---
--- @
--- λ> primitiveType "string"
--- Just String
--- λ> primitiveType "foo"
--- Nothing
--- @
-primitiveType :: Text -> Maybe Type
-primitiveType = \case
-  "null"    -> Just Null
-  "boolean" -> Just Boolean
-  "int"     -> Just Int
-  "long"    -> Just Long
-  "float"   -> Just Float
-  "double"  -> Just Double
-  "bytes"   -> Just Bytes
-  "string"  -> Just String
-  _         -> Nothing
-
 data Field = Field { fldName    :: Text
                    , fldAliases :: [Text]
                    , fldDoc     :: Maybe Text
@@ -565,6 +542,7 @@
   (<>) = mplus
 instance Monoid (Result a) where
   mempty = fail "Empty Result"
+  mappend = (<>)
 instance Foldable Result where
   foldMap _ (Error _)   = mempty
   foldMap f (Success y) = f y
diff --git a/test/Avro/JSONSpec.hs b/test/Avro/JSONSpec.hs
--- a/test/Avro/JSONSpec.hs
+++ b/test/Avro/JSONSpec.hs
@@ -8,6 +8,7 @@
 
 import qualified Data.Aeson           as Aeson
 import qualified Data.ByteString.Lazy as LBS
+import qualified Data.Map             as Map
 
 import           Data.Avro.Deriving
 import           Data.Avro.EitherN
@@ -69,24 +70,26 @@
     let expected = EnumWrapper 37 "blarg" EnumReasonInstead
     parseJSON enumsExampleJSON `shouldBe` pure expected
   let unionsExampleA = Unions
-        { unionsScalars    = Left "blarg"
-        , unionsNullable   = Nothing
-        , unionsRecords    = Left $ Foo { fooStuff = "stuff" }
-        , unionsSameFields = Left $ Foo { fooStuff = "foo stuff" }
-        , unionsThree      = E3_1 37
-        , unionsFour       = E4_2 "foo"
-        , unionsFive       = E5_4 $ Foo { fooStuff = "foo stuff" }
+        { unionsScalars     = Left "blarg"
+        , unionsNullable    = Nothing
+        , unionsRecords     = Left $ Foo { fooStuff = "stuff" }
+        , unionsSameFields  = Left $ Foo { fooStuff = "foo stuff" }
+        , unionsArrayAndMap = Left ["foo"]
+        , unionsThree       = E3_1 37
+        , unionsFour        = E4_2 "foo"
+        , unionsFive        = E5_4 $ Foo { fooStuff = "foo stuff" }
         }
       unionsExampleB = Unions
-        { unionsScalars    = Right 37
-        , unionsNullable   = Just 42
-        , unionsRecords    = Right $ Bar { barStuff = "stuff"
-                                      , barThings = Foo "things"
-                                      }
-        , unionsSameFields = Right $ NotFoo { notFooStuff = "not foo stuff" }
-        , unionsThree      = E3_3 37
-        , unionsFour       = E4_4 $ Foo { fooStuff = "foo stuff" }
-        , unionsFive       = E5_5 $ NotFoo { notFooStuff = "not foo stuff" }
+        { unionsScalars     = Right 37
+        , unionsNullable    = Just 42
+        , unionsRecords     = Right $ Bar { barStuff = "stuff"
+                                          , barThings = Foo "things"
+                                          }
+        , unionsSameFields  = Right $ NotFoo { notFooStuff = "not foo stuff" }
+        , unionsArrayAndMap = Right $ Map.fromList [("a", 5)]
+        , unionsThree       = E3_3 37
+        , unionsFour        = E4_4 $ Foo { fooStuff = "foo stuff" }
+        , unionsFive        = E5_5 $ NotFoo { notFooStuff = "not foo stuff" }
         }
   it "should roundtrip (unions)" $ do
     forM_ [unionsExampleA, unionsExampleB] $ \ msg ->
diff --git a/test/Avro/THUnionSpec.hs b/test/Avro/THUnionSpec.hs
--- a/test/Avro/THUnionSpec.hs
+++ b/test/Avro/THUnionSpec.hs
@@ -14,6 +14,7 @@
 import qualified Data.Avro.Schema     as Schema
 import qualified Data.Avro.Types      as Avro
 import qualified Data.ByteString.Lazy as LBS
+import qualified Data.Map             as Map
 
 import           System.Directory     (doesFileExist)
 
@@ -26,24 +27,26 @@
 spec :: Spec
 spec = describe "Avro.THUnionSpec: Schema with unions." $ do
   let objA = Unions
-        { unionsScalars    = Left "foo"
-        , unionsNullable   = Nothing
-        , unionsRecords    = Left $ Foo { fooStuff = "stuff" }
-        , unionsSameFields = Left $ Foo { fooStuff = "more stuff" }
-        , unionsThree      = E3_1 37
-        , unionsFour       = E4_2 "foo"
-        , unionsFive       = E5_4 $ Foo { fooStuff = "foo stuff" }
+        { unionsScalars     = Left "foo"
+        , unionsNullable    = Nothing
+        , unionsRecords     = Left $ Foo { fooStuff = "stuff" }
+        , unionsSameFields  = Left $ Foo { fooStuff = "more stuff" }
+        , unionsArrayAndMap = Left ["foo"]
+        , unionsThree       = E3_1 37
+        , unionsFour        = E4_2 "foo"
+        , unionsFive        = E5_4 $ Foo { fooStuff = "foo stuff" }
         }
       objB = Unions
-        { unionsScalars    = Right 42
-        , unionsNullable   = Just 37
-        , unionsRecords    = Right $ Bar { barStuff  = "stuff"
-                                         , barThings = Foo { fooStuff = "things" }
-                                       }
-        , unionsSameFields = Right $ NotFoo { notFooStuff = "different from Foo" }
-        , unionsThree      = E3_3 37
-        , unionsFour       = E4_4 $ Foo { fooStuff = "foo stuff" }
-        , unionsFive       = E5_5 $ NotFoo { notFooStuff = "not foo stuff" }
+        { unionsScalars     = Right 42
+        , unionsNullable    = Just 37
+        , unionsRecords     = Right $ Bar { barStuff  = "stuff"
+                                          , barThings = Foo { fooStuff = "things" }
+                                          }
+        , unionsSameFields  = Right $ NotFoo { notFooStuff = "different from Foo" }
+        , unionsArrayAndMap = Right $ Map.fromList [("a", 5)]
+        , unionsThree       = E3_3 37
+        , unionsFour        = E4_4 $ Foo { fooStuff = "foo stuff" }
+        , unionsFive        = E5_5 $ NotFoo { notFooStuff = "not foo stuff" }
         }
 
       field name schema def = Schema.Field name [] Nothing (Just Schema.Ascending) schema def
@@ -54,10 +57,11 @@
       foo    = named "haskell.avro.example.Foo"
       notFoo = named "haskell.avro.example.NotFoo"
       expectedSchema = record "haskell.avro.example.Unions"
-        [ field "scalars"    (Schema.mkUnion (NE.fromList [Schema.String, Schema.Long])) scalarsDefault
-        , field "nullable"   (Schema.mkUnion (NE.fromList [Schema.Null, Schema.Int]))    nullableDefault
-        , field "records"    (Schema.mkUnion (NE.fromList [fooSchema, barSchema]))       Nothing
-        , field "sameFields" (Schema.mkUnion (NE.fromList [foo, notFooSchema]))          Nothing
+        [ field "scalars"     (Schema.mkUnion (NE.fromList [Schema.String, Schema.Long])) scalarsDefault
+        , field "nullable"    (Schema.mkUnion (NE.fromList [Schema.Null, Schema.Int]))    nullableDefault
+        , field "records"     (Schema.mkUnion (NE.fromList [fooSchema, barSchema]))       Nothing
+        , field "sameFields"  (Schema.mkUnion (NE.fromList [foo, notFooSchema]))          Nothing
+        , field "arrayAndMap" (Schema.mkUnion (NE.fromList [array, map]))                 Nothing
 
         , field "three" (Schema.mkUnion (NE.fromList [Schema.Int, Schema.String, Schema.Long]))              Nothing
         , field "four"  (Schema.mkUnion (NE.fromList [Schema.Int, Schema.String, Schema.Long, foo]))         Nothing
@@ -72,6 +76,9 @@
         , field "things" (named "haskell.avro.example.Foo") Nothing
         ]
       notFooSchema = record "haskell.avro.example.NotFoo" [field "stuff" Schema.String Nothing]
+
+      array = Schema.Array { Schema.item = Schema.String }
+      map   = Schema.Map { Schema.values = Schema.Long }
 
   unionsSchemaFile <- runIO $ getFileName "test/data/unions.avsc" >>= LBS.readFile
   let Just unionsSchemaFromJSON = Aeson.decode unionsSchemaFile
diff --git a/test/data/unions-object-a.json b/test/data/unions-object-a.json
--- a/test/data/unions-object-a.json
+++ b/test/data/unions-object-a.json
@@ -13,6 +13,9 @@
       "stuff": "foo stuff"
     }
   },
+  "arrayAndMap" : {
+    "array" : ["foo"]
+  },
   "three": { "int": 37 },
   "four": { "string": "foo" },
   "five": {
diff --git a/test/data/unions-object-b.json b/test/data/unions-object-b.json
--- a/test/data/unions-object-b.json
+++ b/test/data/unions-object-b.json
@@ -18,6 +18,9 @@
       "stuff": "not foo stuff"
     }
   },
+  "arrayAndMap" : {
+    "map" : { "a" : 5 }
+  },
   "three": { "long": 37 },
   "four": {
     "haskell.avro.example.Foo": { "stuff" : "foo stuff" }
diff --git a/test/data/unions.avsc b/test/data/unions.avsc
--- a/test/data/unions.avsc
+++ b/test/data/unions.avsc
@@ -44,6 +44,16 @@
         }
       ]
     },
+    { "name" : "arrayAndMap",
+      "type" : [
+        { "type" : "array",
+          "items" : "string"
+        },
+        { "type" : "map",
+          "values" : "long"
+        }
+      ]
+    },
     { "name" : "three", "type" : ["int", "string", "long"] },
     { "name" : "four", "type" : ["int", "string", "long", "Foo"] },
     { "name" : "five", "type" : ["int", "string", "long", "Foo", "NotFoo"] }
