diff --git a/ginger.cabal b/ginger.cabal
--- a/ginger.cabal
+++ b/ginger.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                ginger
-version:             0.7.1.0
+version:             0.7.2.0
 synopsis:            An implementation of the Jinja2 template language in Haskell
 description:         Ginger is Jinja, minus the most blatant pythonisms. Wants
                      to be feature complete, but isn't quite there yet.
@@ -36,6 +36,7 @@
   -- other-extensions:
   build-depends: base >=4.8 && <5
                , aeson
+               , aeson-pretty
                , bytestring
                , data-default >= 0.5
                , filepath >= 1.3
diff --git a/src/Text/Ginger/Run.hs b/src/Text/Ginger/Run.hs
--- a/src/Text/Ginger/Run.hs
+++ b/src/Text/Ginger/Run.hs
@@ -150,6 +150,7 @@
     , ("int", fromFunction . unaryFunc $ toGVal . fmap (Prelude.truncate :: Scientific -> Int) . asNumber)
     , ("int_ratio", fromFunction . variadicNumericFunc 1 $ fromIntegral . intRatio . Prelude.map Prelude.floor)
     , ("iterable", fromFunction . unaryFunc $ toGVal . (\x -> isList x || isDict x))
+    , ("json", fromFunction gfnJSON)
     , ("length", fromFunction . unaryFunc $ toGVal . length)
     , ("less", fromFunction gfnLess)
     , ("lessEquals", fromFunction gfnLessEquals)
diff --git a/src/Text/Ginger/Run/Builtins.hs b/src/Text/Ginger/Run/Builtins.hs
--- a/src/Text/Ginger/Run/Builtins.hs
+++ b/src/Text/Ginger/Run/Builtins.hs
@@ -46,6 +46,7 @@
 import Data.String (fromString)
 import qualified Data.Text as Text
 import qualified Data.ByteString.UTF8 as UTF8
+import qualified Data.ByteString.Lazy as LBS
 import Control.Monad
 import Control.Monad.Identity
 import Control.Monad.Writer
@@ -77,6 +78,8 @@
                  , TimeZone (..)
                  )
 import Data.Foldable (asum)
+import qualified Data.Aeson as JSON
+import qualified Data.Aeson.Encode.Pretty as JSON
 
 tshow :: Show a => a -> Text
 tshow = Text.pack . show
@@ -618,3 +621,27 @@
                     (if sortByKey then fst else (asText . snd))
             return . orderedDict . List.sortOn projection $ items
         _ -> throwHere $ ArgumentsError (Just "dictsort") "expected: (dict, case_sensitive=false, by=null)"
+
+gfnJSON :: Monad m => Function (Run p m h)
+gfnJSON args =
+  let extracted =
+          extractArgsDefL
+            [ ("val", def)
+            , ("pretty", toGVal True)
+            ]
+            args
+  in case extracted of
+      Right [ gVal, gPretty ] -> do
+          let encoder =
+                if asBoolean gPretty then
+                    JSON.encodePretty
+                else
+                    JSON.encode
+          return .
+            toGVal .
+            Text.pack .
+            UTF8.toString .
+            LBS.toStrict .
+            encoder $
+            gVal
+      Left _ -> throwHere $ ArgumentsError (Just "json") "expected: one argument"
diff --git a/test/Text/Ginger/SimulationTests.hs b/test/Text/Ginger/SimulationTests.hs
--- a/test/Text/Ginger/SimulationTests.hs
+++ b/test/Text/Ginger/SimulationTests.hs
@@ -295,7 +295,7 @@
         , testCase "AND (neither)" $ do
             mkTestHtml [] [] "{% if 0 && 0 %}yes{% else %}no{% endif %}" "no"
         ]
-    , testGroup "Boolean AND"
+    , testGroup "Boolean OR"
         [ testCase "OR (both)" $ do
             mkTestHtml [] [] "{% if 1 || 2 %}yes{% else %}no{% endif %}" "yes"
         , testCase "OR (only one)" $ do
@@ -634,11 +634,11 @@
         -- TODO
         -- \"printf\"
         , testGroup "\"printf\""
-            [ testCase "%s, passed as int" $ do
+            [ testCase "%i, passed as int" $ do
                 mkTestHtml [] []
                     "{{ printf(\"%i\", 1) }}"
                     "1"
-            , testCase "%s, passed as string" $ do
+            , testCase "%i, passed as string" $ do
                 mkTestHtml [] []
                     "{{ printf(\"%i\", \"1\") }}"
                     "1"
@@ -786,6 +786,16 @@
                 mkTestHtml [] []
                     "{{ replace('foobar', 'o') }}"
                     "fbar"
+            ]
+        , testGroup "\"json\""
+            [ testCase "null" $ do
+                mkTestHtml [] []
+                    "{{ null|json }}"
+                    "null"
+            , testCase "[1,2,3]" $ do
+                mkTestHtml [] []
+                    "{{ [1,2,3]|json(false) }}"
+                    "[1,2,3]"
             ]
         ]
     , testGroup "Setting variables"
