on master help sign in
Per @basvandijk, I've moved all of the logic into a camelTo function.
63c09caGabe McArthur
Data/Aeson/TH.hs+2−46
@@ -70,11 +70,6 @@ module Data.Aeson.TH
70
70
71 , mkToJSON
71 , mkToJSON
72 , mkParseJSON
72 , mkParseJSON
73−
74− -- * For the oft-conversion of CamelCase to underscore_case in JSON
75− , camelToUnderscore
76− , camelToUnderscoreDrop
77− , camelToUnderscoreDropChecked
78 ) where
73 ) where
79
74
80--------------------------------------------------------------------------------
75--------------------------------------------------------------------------------
@@ -95,7 +90,6 @@ import Data.Aeson.Types ( Value(..), Parser
95-- from base:
90-- from base:
96import Control.Applicative ( pure, (<$>), (<*>) )
91import Control.Applicative ( pure, (<$>), (<*>) )
97import Control.Monad ( return, mapM, liftM2, fail )
92import Control.Monad ( return, mapM, liftM2, fail )
98−import Data.Char ( isUpper, toLower)
99import Data.Bool ( Bool(False, True), otherwise, (&&) )
93import Data.Bool ( Bool(False, True), otherwise, (&&) )
100import Data.Eq ( (==) )
94import Data.Eq ( (==) )
101import Data.Function ( ($), (.) )
95import Data.Function ( ($), (.) )
@@ -103,10 +97,10 @@ import Data.Functor ( fmap )
103import Data.Int ( Int )
97import Data.Int ( Int )
104import Data.Either ( Either(Left, Right) )
98import Data.Either ( Either(Left, Right) )
105import Data.List ( (++), foldl, foldl', intercalate
99import Data.List ( (++), foldl, foldl', intercalate
106− , length, map, zip, genericLength, all, partition, drop
100+ , length, map, zip, genericLength, all, partition
107 )
101 )
108import Data.Maybe ( Maybe(Nothing, Just), catMaybes )
102import Data.Maybe ( Maybe(Nothing, Just), catMaybes )
109−import Prelude ( String, (-), Integer, fromIntegral, error, const )
103+import Prelude ( String, (-), Integer, fromIntegral, error )
110import Text.Printf ( printf )
104import Text.Printf ( printf )
111import Text.Show ( show )
105import Text.Show ( show )
112#if __GLASGOW_HASKELL__ < 700
106#if __GLASGOW_HASKELL__ < 700
@@ -878,41 +872,3 @@ valueConName (String _) = "String"
878valueConName (Number _) = "Number"
872valueConName (Number _) = "Number"
879valueConName (Bool _) = "Boolean"
873valueConName (Bool _) = "Boolean"
880valueConName Null = "Null"
874valueConName Null = "Null"
881−
882−-- | Removes the first Int characters of string, then converts from
883−-- CamelCase to underscore_case.
884−--
885−-- For use by Aeson template haskell calls.
886−camelToUnderscore :: String -> String
887−camelToUnderscore = camelToUnderscoreDrop 0
888−
889−-- | Removes the first Int characters of string, then converts from
890−-- CamelCase to underscore_case. Drops the first few characters from
891−-- the input.
892−--
893−-- For use by Aeson template haskell calls.
894−camelToUnderscoreDrop :: Int -> String -> String
895−camelToUnderscoreDrop i = camelToUnderscoreDropChecked (const i)
896−
897−-- | Removes the first Int characters of string, then converts from
898−-- CamelCase to underscore_case. Investigates the input string to
899−-- see how many characters to drop.
900−--
901−-- For use by Aeson template haskell calls.
902−camelToUnderscoreDropChecked :: (String -> Int) -> String -> String
903−camelToUnderscoreDropChecked chk property =
904− underscore True input
905− where
906− input :: String
907− input = drop (chk property) property
908−
909− underscore :: Bool -- ^ Previous was a capital letter
910− -> String -- ^ The remaining string
911− -> String
912− underscore _ [] = []
913− underscore prev (x : xs) = if isUpper x
914− then if prev
915− then toLower x : underscore True xs
916− else '_' : toLower x : underscore True xs
917− else x : underscore False xs
918−
Data/Aeson/Types/Internal.hs+35−9
@@ -35,19 +35,23 @@ module Data.Aeson.Types.Internal
35 , SumEncoding(..)
35 , SumEncoding(..)
36 , defaultOptions
36 , defaultOptions
37 , defaultTaggedObject
37 , defaultTaggedObject
38+
39+ -- Used for changing CamelCase names into something else.
40+ , camelTo
38 ) where
41 ) where
39
42
40import Control.Applicative
43import Control.Applicative
41import Control.Monad
44import Control.Monad
42−import Control.DeepSeq (NFData(..))
45+import Control.DeepSeq ( NFData(..) )
43−import Data.Attoparsec.Char8 (Number(..))
46+import Data.Attoparsec.Char8 ( Number(..) )
47+import Data.Char ( isUpper, toLower )
44−import Data.Hashable (Hashable(..))
48+import Data.Hashable ( Hashable(..) )
45−import Data.HashMap.Strict (HashMap)
49+import Data.HashMap.Strict ( HashMap )
46−import Data.Monoid (Monoid(..))
50+import Data.Monoid ( Monoid(..) )
47−import Data.String (IsString(..))
51+import Data.String ( IsString(..) )
48−import Data.Text (Text, pack)
52+import Data.Text ( Text, pack )
49−import Data.Typeable (Typeable)
53+import Data.Typeable ( Typeable )
50−import Data.Vector (Vector)
54+import Data.Vector ( Vector )
51import qualified Data.HashMap.Strict as H
55import qualified Data.HashMap.Strict as H
52import qualified Data.Vector as V
56import qualified Data.Vector as V
53
57
@@ -328,3 +332,25 @@ defaultTaggedObject = TaggedObject
328 { tagFieldName = "tag"
332 { tagFieldName = "tag"
329 , contentsFieldName = "contents"
333 , contentsFieldName = "contents"
330 }
334 }
335+
336+-- | Converts from CamelCase to another lower case, interpsersing
337+-- the character between all capital letters and their previous
338+-- entries, except those capital letters that appear together,
339+-- like 'API'.
340+--
341+-- For use by Aeson template haskell calls.
342+--
343+-- > camelTo '_' 'CamelCaseAPI' == "camel_case_api"
344+camelTo :: Char -> String -> String
345+camelTo c = lastWasCap True
346+ where
347+ lastWasCap :: Bool -- ^ Previous was a capital letter
348+ -> String -- ^ The remaining string
349+ -> String
350+ lastWasCap _ [] = []
351+ lastWasCap prev (x : xs) = if isUpper x
352+ then if prev
353+ then toLower x : lastWasCap True xs
354+ else c : toLower x : lastWasCap True xs
355+ else x : lastWasCap False xs
356+