diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,18 +1,17 @@
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE NamedFieldPuns #-}
 
 module Main where
 
-import Hadolint.Formatter
 import Hadolint.Rules
 import Language.Docker.Parser
 import Language.Docker.Syntax
 
 import Control.Applicative
 import Control.Monad (filterM)
-import Data.List (sort)
 import Data.Maybe (listToMaybe)
-import Data.Semigroup
+import Data.Semigroup ((<>))
 import qualified Data.Version as V (showVersion)
 import qualified Data.Yaml as Yaml
 import Development.GitRev (gitDescribe)
@@ -22,15 +21,28 @@
 import System.Directory
        (XdgDirectory(..), doesFileExist, getCurrentDirectory,
         getXdgDirectory)
-import System.Environment (getArgs)
-import System.Exit hiding (die)
+import System.Exit (exitFailure, exitSuccess)
 import System.FilePath ((</>))
 import Text.Parsec (ParseError)
 
+import qualified Hadolint.Formatter.Checkstyle as Checkstyle
+import qualified Hadolint.Formatter.Codeclimate as Codeclimate
+import Hadolint.Formatter.Format (toResult)
+import qualified Hadolint.Formatter.Json as Json
+import qualified Hadolint.Formatter.TTY as TTY
+
 type IgnoreRule = String
 
+data OutputFormat
+    = Json
+    | TTY
+    | CodeclimateJson
+    | Checkstyle
+    deriving (Show, Eq)
+
 data LintOptions = LintOptions
     { showVersion :: Bool
+    , format :: OutputFormat
     , ignoreRules :: [IgnoreRule]
     , dockerfiles :: [String]
     } deriving (Show)
@@ -44,22 +56,49 @@
 ignoreFilter :: [IgnoreRule] -> RuleCheck -> Bool
 ignoreFilter ignoredRules (RuleCheck (Metadata code _ _) _ _ _) = code `notElem` ignoredRules
 
-printChecks :: [RuleCheck] -> IO ()
-printChecks checks = do
-    mapM_ (putStrLn . formatCheck) $ sort checks
-    if null checks
-        then exitSuccess
-        else die
+toOutputFormat :: String -> Maybe OutputFormat
+toOutputFormat "json" = Just Json
+toOutputFormat "tty" = Just TTY
+toOutputFormat "codeclimate" = Just CodeclimateJson
+toOutputFormat "checkstyle" = Just Checkstyle
+toOutputFormat _ = Nothing
 
+showFormat :: OutputFormat -> String
+showFormat Json = "json"
+showFormat TTY = "tty"
+showFormat CodeclimateJson = "codeclimate"
+showFormat Checkstyle = "checkstyle"
+
 parseOptions :: Parser LintOptions
 parseOptions =
     LintOptions <$> -- CLI options parser definition
-    switch (long "version" <> short 'v' <> help "Show version") <*>
-    many
-        (strOption
-             (long "ignore" <> help "Ignore rule. If present, config file is ignored" <>
-              metavar "RULECODE")) <*>
-    many (argument str (metavar "DOCKERFILE..."))
+    version <*>
+    outputFormat <*>
+    ignoreList <*>
+    files
+  where
+    version = switch (long "version" <> short 'v' <> help "Show version")
+    --
+    -- | Parse the output format option
+    outputFormat =
+        option
+            (maybeReader toOutputFormat)
+            (long "format" <> -- options for the output format
+             short 'f' <>
+             help "The output format for the results [tty | json | checkstyle | codeclimate]" <>
+             value TTY <> -- The default value
+             showDefaultWith showFormat <>
+             completeWith ["tty", "json", "checkstyle", "codeclimate"])
+    --
+    -- | Parse a list of ignored rules
+    ignoreList =
+        many
+            (strOption
+                 (long "ignore" <> help "Ignore rule. If present, config file is ignored" <>
+                  metavar "RULECODE"))
+    --
+    -- | Parse a list of dockerfile names
+    files = many (argument str (metavar "DOCKERFILE..." <> action "file"))
 
 main :: IO ()
 main = execParser opts >>= applyConfig >>= lint
@@ -71,7 +110,7 @@
              header "hadolint - Dockerfile Linter written in Haskell")
 
 applyConfig :: LintOptions -> IO LintOptions
-applyConfig o@(LintOptions _ (_:_) _) = return o
+applyConfig o@LintOptions {ignoreRules = (_:_)} = return o
 applyConfig o = do
     theConfig <- findConfig
     case theConfig of
@@ -105,10 +144,14 @@
 parseFilename "-" = "/dev/stdin"
 parseFilename s = s
 
-lintDockerfile :: [IgnoreRule] -> String -> IO ()
+lintDockerfile :: [IgnoreRule] -> String -> IO (Either ParseError [RuleCheck])
 lintDockerfile ignoreRules dockerfile = do
     ast <- parseFile $ parseFilename dockerfile
-    checkAst (ignoreFilter ignoreRules) ast
+    return (processedFile ast)
+  where
+    processedFile = fmap processRules
+    processRules dockerfile = filter ignored (analyzeAll dockerfile)
+    ignored = ignoreFilter ignoreRules
 
 getVersion :: String
 getVersion
@@ -117,15 +160,25 @@
     | otherwise = "Haskell Dockerfile Linter " ++ $(gitDescribe)
 
 lint :: LintOptions -> IO ()
-lint (LintOptions True _ _) = putStrLn getVersion >> exitSuccess
-lint (LintOptions _ _ []) = putStrLn "Please provide a Dockerfile" >> exitFailure
-lint (LintOptions _ ignore dfiles) = mapM_ (lintDockerfile ignore) dfiles
-
-checkAst :: (RuleCheck -> Bool) -> Either ParseError Dockerfile -> IO ()
-checkAst checkFilter ast =
-    case ast of
-        Left err -> putStrLn (formatError err) >> exitFailure
-        Right dockerfile -> printChecks $ filter checkFilter $ analyzeAll dockerfile
+lint LintOptions {showVersion = True} = putStrLn getVersion >> exitSuccess
+lint LintOptions {dockerfiles = []} = putStrLn "Please provide a Dockerfile" >> exitFailure
+lint LintOptions {ignoreRules = ignoreList, dockerfiles = dFiles, format} = do
+    processedFiles <- mapM (lintDockerfile ignoreList) dFiles
+    let allResults = results processedFiles
+    printResult allResults
+    if allResults /= mempty
+        then exitFailure
+        else exitSuccess
+  where
+    results = foldMap toResult -- Parse and check rules for each dockerfile,
+                               -- then convert them to a Result and combine with
+                               -- the result of the previous dockerfile results
+    printResult res =
+        case format of
+            TTY -> TTY.printResult res
+            Json -> Json.printResult res
+            Checkstyle -> Checkstyle.printResult res
+            CodeclimateJson -> Codeclimate.printResult res >> exitSuccess
 
 analyzeAll :: Dockerfile -> [RuleCheck]
 analyzeAll = analyze rules
@@ -134,6 +187,3 @@
 analyzeEither :: Either t Dockerfile -> [RuleCheck]
 analyzeEither (Left err) = []
 analyzeEither (Right dockerfile) = analyzeAll dockerfile
-
-die :: IO a
-die = exitWith (ExitFailure 1)
diff --git a/hadolint.cabal b/hadolint.cabal
--- a/hadolint.cabal
+++ b/hadolint.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 27e3b32590f30c0792f229a5aebdd2796f20c9ef9889029a1f2c6dbd2d986bcf
+-- hash: 0e73d59878f7e4b0a47b9ea2607d8a8de24ade73e92179f9c23870e7e0364c35
 
 name:           hadolint
-version:        1.4.0
+version:        1.5.0
 synopsis:       Dockerfile Linter JavaScript API
 description:    A smarter Dockerfile linter that helps you build best practice Docker images.
 category:       Development
@@ -30,15 +30,22 @@
       src
   build-depends:
       ShellCheck >=0.4.7
+    , aeson
     , base >=4.8 && <5
-    , bytestring >=0.10
+    , bytestring
+    , dlist
     , language-docker >=2.0.0
     , parsec >=3.1
     , split >=0.2
+    , text
   exposed-modules:
       Hadolint.Bash
-      Hadolint.Formatter
       Hadolint.Rules
+      Hadolint.Formatter.Format
+      Hadolint.Formatter.Json
+      Hadolint.Formatter.TTY
+      Hadolint.Formatter.Codeclimate
+      Hadolint.Formatter.Checkstyle
   other-modules:
       Paths_hadolint
   default-language: Haskell2010
@@ -71,6 +78,7 @@
   build-depends:
       HUnit >=1.2
     , ShellCheck >=0.4.7
+    , aeson
     , base >=4.8 && <5
     , bytestring >=0.10
     , hadolint
diff --git a/src/Hadolint/Formatter.hs b/src/Hadolint/Formatter.hs
deleted file mode 100644
--- a/src/Hadolint/Formatter.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-module Hadolint.Formatter
-    ( formatCheck
-    , formatError
-    ) where
-
-import Control.Monad
-import Data.Char (isSpace)
-import Hadolint.Rules
-import Language.Docker.Syntax
-import Text.Parsec.Error
-       (Message, ParseError, errorMessages, errorPos, messageString,
-        showErrorMessages)
-import Text.Parsec.Pos
-
-formatError :: ParseError -> String
-formatError err = posPart ++ stripNewlines msgPart
-  where
-    pos = errorPos err
-    posPart = sourceName pos ++ ":" ++ show (sourceLine pos) ++ ":" ++ show (sourceColumn pos)
-    msgPart =
-        showErrorMessages
-            "or"
-            "unknown parse error"
-            "expecting"
-            "unexpected"
-            "end of input"
-            (errorMessages err)
-    stripNewlines =
-        map
-            (\c ->
-                 if c == '\n'
-                     then ' '
-                     else c)
-
-formatCheck :: RuleCheck -> String
-formatCheck (RuleCheck metadata source linenumber _) =
-    formatPos source linenumber ++ code metadata ++ " " ++ message metadata
-
-formatPos :: Filename -> Linenumber -> String
-formatPos source linenumber =
-    if linenumber >= 0
-        then source ++ ":" ++ show linenumber ++ " "
-        else source ++ " "
diff --git a/src/Hadolint/Formatter/Checkstyle.hs b/src/Hadolint/Formatter/Checkstyle.hs
new file mode 100644
--- /dev/null
+++ b/src/Hadolint/Formatter/Checkstyle.hs
@@ -0,0 +1,91 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Hadolint.Formatter.Checkstyle
+    ( printResult
+    , formatResult
+    ) where
+
+import qualified Data.ByteString.Builder as Builder
+import qualified Data.ByteString.Lazy.Char8 as B
+import Data.Char
+import Data.DList (toList)
+import Data.List (groupBy)
+import Data.Monoid ((<>), mconcat)
+import Hadolint.Formatter.Format
+import Hadolint.Rules (Metadata(..), RuleCheck(..))
+import ShellCheck.Interface
+import Text.Parsec (ParseError)
+import Text.Parsec.Error (ParseError, errorPos)
+import Text.Parsec.Pos
+
+data CheckStyle = CheckStyle
+    { file :: String
+    , line :: Int
+    , column :: Int
+    , impact :: String
+    , msg :: String
+    , source :: String
+    }
+
+errorToCheckStyle :: ParseError -> CheckStyle
+errorToCheckStyle err =
+    CheckStyle
+    { file = sourceName pos
+    , line = sourceLine pos
+    , column = sourceColumn pos
+    , impact = severityText ErrorC
+    , msg = stripNewlines (formatErrorReason err)
+    , source = "DL1000"
+    }
+  where
+    pos = errorPos err
+
+ruleToCheckStyle :: RuleCheck -> CheckStyle
+ruleToCheckStyle RuleCheck {..} =
+    CheckStyle
+    { file = filename
+    , line = linenumber
+    , column = 1
+    , impact = severityText (severity metadata)
+    , msg = message metadata
+    , source = code metadata
+    }
+
+toXml :: [CheckStyle] -> Builder.Builder
+toXml checks = wrap fileName (foldMap convert checks)
+  where
+    wrap name innerNode = "<file " <> attr "name" name <> ">" <> innerNode <> "</file>"
+    convert CheckStyle {..} =
+        "<error " <> -- Beging the node construction
+        attr "line" (show line) <>
+        attr "column" (show column) <>
+        attr "severity" impact <>
+        attr "message" msg <>
+        attr "source" source <>
+        "/>"
+    fileName = file (head checks)
+
+attr name value = Builder.string8 name <> "='" <> Builder.string8 (escape value) <> "' "
+
+escape = concatMap doEscape
+  where
+    doEscape c =
+        if isOk c
+            then [c]
+            else "&#" ++ show (ord c) ++ ";"
+    isOk x = any ($x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])]
+
+formatResult :: Result -> Builder.Builder
+formatResult (Result errors checks) =
+    "<?xml version='1.0' encoding='UTF-8'?><checkstyle version='4.3'>" <> xmlBody <> "</checkstyle>"
+  where
+    xmlBody = mconcat xmlChunks
+    xmlChunks = fmap toXml (groupBy sameFileName flatten)
+    flatten = toList $ checkstyleErrors <> checkstyleChecks
+    checkstyleErrors = fmap errorToCheckStyle errors
+    checkstyleChecks = fmap ruleToCheckStyle checks
+    sameFileName CheckStyle {file = f1} CheckStyle {file = f2} = f1 == f2
+
+printResult :: Result -> IO ()
+printResult result = B.putStr (Builder.toLazyByteString (formatResult result))
diff --git a/src/Hadolint/Formatter/Codeclimate.hs b/src/Hadolint/Formatter/Codeclimate.hs
new file mode 100644
--- /dev/null
+++ b/src/Hadolint/Formatter/Codeclimate.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveGeneric #-}
+
+module Hadolint.Formatter.Codeclimate
+    ( printResult
+    , formatResult
+    ) where
+
+import Data.Aeson hiding (Result)
+import qualified Data.ByteString.Lazy as B
+import Data.DList (DList)
+import Data.Monoid ((<>))
+import GHC.Generics
+import Hadolint.Formatter.Format (Result(..), formatErrorReason)
+import Hadolint.Rules (Metadata(..), RuleCheck(..))
+import ShellCheck.Interface
+import Text.Parsec.Error (errorPos)
+import Text.Parsec.Pos
+
+data Issue = Issue
+    { checkName :: String
+    , description :: String
+    , location :: Location
+    , impact :: String
+    }
+
+data Location
+    = LocLine String
+              Int
+    | LocPos String
+             Pos
+
+instance ToJSON Location where
+    toJSON (LocLine path l) = object ["path" .= path, "lines" .= object ["begin" .= l, "end" .= l]]
+    toJSON (LocPos path pos) =
+        object ["path" .= path, "positions" .= object ["begin" .= pos, "end" .= pos]]
+
+data Pos = Pos
+    { line :: Int
+    , column :: Int
+    } deriving (Generic)
+
+instance ToJSON Pos
+
+instance ToJSON Issue where
+    toJSON Issue {..} =
+        object
+            [ "type" .= ("issue" :: String)
+            , "check_name" .= checkName
+            , "description" .= description
+            , "categories" .= (["Bug Risk"] :: [String])
+            , "location" .= location
+            , "severity" .= impact
+            ]
+
+errorToIssue err =
+    Issue
+    { checkName = "DL1000"
+    , description = formatErrorReason err
+    , location = LocPos (sourceName pos) (Pos (sourceLine pos) (sourceColumn pos))
+    , impact = severityText ErrorC
+    }
+  where
+    pos = errorPos err
+
+checkToIssue RuleCheck {..} =
+    Issue
+    { checkName = code metadata
+    , description = message metadata
+    , location = LocLine filename linenumber
+    , impact = severityText (severity metadata)
+    }
+
+severityText :: Severity -> String
+severityText severity =
+    case severity of
+        ErrorC -> "blocker"
+        WarningC -> "major"
+        InfoC -> "info"
+        StyleC -> "minor"
+
+formatResult :: Result -> DList Issue
+formatResult (Result errors checks) = allIssues
+  where
+    allIssues = errorMessages <> checkMessages
+    errorMessages = fmap errorToIssue errors
+    checkMessages = fmap checkToIssue checks
+
+printResult :: Result -> IO ()
+printResult result = mapM_ output (formatResult result)
+  where
+    output value = do
+        B.putStr (encode value)
+        B.putStr (B.singleton 0x00)
diff --git a/src/Hadolint/Formatter/Format.hs b/src/Hadolint/Formatter/Format.hs
new file mode 100644
--- /dev/null
+++ b/src/Hadolint/Formatter/Format.hs
@@ -0,0 +1,56 @@
+module Hadolint.Formatter.Format
+    ( formatErrorReason
+    , severityText
+    , stripNewlines
+    , Result(..)
+    , toResult
+    ) where
+
+import Data.DList (DList, fromList, singleton)
+import Data.List (sort)
+import Data.Monoid
+import Hadolint.Rules
+import ShellCheck.Interface
+import Text.Parsec.Error
+       (ParseError, errorMessages, showErrorMessages)
+
+data Result = Result
+    { errors :: DList ParseError
+    , checks :: DList RuleCheck
+    } deriving (Eq)
+
+instance Monoid Result where
+    mempty = Result mempty mempty
+    mappend (Result e1 c1) (Result e2 c2) = Result (e1 <> e2) (c1 <> c2)
+
+toResult :: Either ParseError [RuleCheck] -> Result
+toResult res =
+    case res of
+        Left err -> Result (singleton err) mempty
+        Right c -> Result mempty (fromList (sort c))
+
+severityText :: Severity -> String
+severityText severity =
+    case severity of
+        ErrorC -> "error"
+        WarningC -> "warning"
+        InfoC -> "info"
+        StyleC -> "style"
+
+formatErrorReason :: ParseError -> String
+formatErrorReason err =
+    showErrorMessages
+        "or"
+        "unknown parse error"
+        "expecting"
+        "unexpected"
+        "end of input"
+        (errorMessages err)
+
+stripNewlines :: String -> String
+stripNewlines =
+    map
+        (\c ->
+             if c == '\n'
+                 then ' '
+                 else c)
diff --git a/src/Hadolint/Formatter/Json.hs b/src/Hadolint/Formatter/Json.hs
new file mode 100644
--- /dev/null
+++ b/src/Hadolint/Formatter/Json.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Hadolint.Formatter.Json
+    ( printResult
+    , formatResult
+    ) where
+
+import Data.Aeson hiding (Result)
+import qualified Data.ByteString.Lazy.Char8 as B
+import Data.Monoid ((<>))
+import Hadolint.Formatter.Format
+       (Result(..), formatErrorReason, severityText)
+import Hadolint.Rules (Metadata(..), RuleCheck(..))
+import ShellCheck.Interface
+import Text.Parsec (ParseError)
+import Text.Parsec.Error (ParseError, errorPos)
+import Text.Parsec.Pos
+
+data JsonFormat
+    = JsonCheck RuleCheck
+    | JsonParseError ParseError
+
+instance ToJSON JsonFormat where
+    toJSON (JsonCheck RuleCheck {..}) =
+        object
+            [ "file" .= filename
+            , "line" .= linenumber
+            , "column" .= (1 :: Int)
+            , "level" .= severityText (severity metadata)
+            , "code" .= code metadata
+            , "message" .= message metadata
+            ]
+    toJSON (JsonParseError err) =
+        object
+            [ "file" .= sourceName pos
+            , "line" .= sourceLine pos
+            , "column" .= sourceColumn pos
+            , "level" .= severityText ErrorC
+            , "code" .= ("DL1000" :: String)
+            , "message" .= formatErrorReason err
+            ]
+      where
+        pos = errorPos err
+
+formatResult :: Result -> Value
+formatResult (Result errors checks) = toJSON allMessages
+  where
+    allMessages = errorMessages <> checkMessages
+    errorMessages = fmap JsonParseError errors
+    checkMessages = fmap JsonCheck checks
+
+printResult :: Result -> IO ()
+printResult result = B.putStrLn (encode (formatResult result))
diff --git a/src/Hadolint/Formatter/TTY.hs b/src/Hadolint/Formatter/TTY.hs
new file mode 100644
--- /dev/null
+++ b/src/Hadolint/Formatter/TTY.hs
@@ -0,0 +1,37 @@
+module Hadolint.Formatter.TTY
+    ( printResult
+    , formatError
+    ) where
+
+import Hadolint.Formatter.Format
+import Hadolint.Rules
+import Language.Docker.Syntax
+import Text.Parsec.Error (ParseError, errorPos)
+import Text.Parsec.Pos
+
+formatErrors :: Functor t => t ParseError -> t String
+formatErrors = fmap formatError
+
+formatError :: ParseError -> String
+formatError err = posPart ++ stripNewlines (formatErrorReason err)
+  where
+    pos = errorPos err
+    posPart = sourceName pos ++ ":" ++ show (sourceLine pos) ++ ":" ++ show (sourceColumn pos)
+
+formatChecks :: Functor t => t RuleCheck -> t String
+formatChecks = fmap formatCheck
+  where
+    formatCheck (RuleCheck metadata source linenumber _) =
+        formatPos source linenumber ++ code metadata ++ " " ++ message metadata
+
+formatPos :: Filename -> Linenumber -> String
+formatPos source linenumber =
+    if linenumber >= 0
+        then source ++ ":" ++ show linenumber ++ " "
+        else source ++ " "
+
+printResult :: Result -> IO ()
+printResult (Result errors checks) = printErrors >> printChecks
+  where
+    printErrors = mapM_ putStrLn (formatErrors errors)
+    printChecks = mapM_ putStrLn (formatChecks checks)
diff --git a/src/Hadolint/Rules.hs b/src/Hadolint/Rules.hs
--- a/src/Hadolint/Rules.hs
+++ b/src/Hadolint/Rules.hs
@@ -1,11 +1,9 @@
 module Hadolint.Rules where
 
 import Control.Arrow ((&&&))
-import Data.List
-       (intercalate, isInfixOf, isPrefixOf, isSuffixOf, mapAccumL)
+import Data.List (isInfixOf, isPrefixOf, isSuffixOf, mapAccumL)
 import Data.List.NonEmpty (toList)
-import Data.List.Split (splitOn, splitOneOf)
-import Data.Maybe (fromMaybe, isJust, mapMaybe)
+import Data.List.Split (splitOneOf)
 import Hadolint.Bash
 import Language.Docker.Syntax
 
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,7 +1,7 @@
 import Test.HUnit hiding (Label)
 import Test.Hspec
 
-import Hadolint.Formatter
+import Hadolint.Formatter.TTY (formatError)
 import Hadolint.Rules
 
 import Language.Docker.Parser
