on master help sign in
Per @basvandijk, I've moved all of the logic into a camelTo function.
commit63c09ca5ab0145b21a51ced5c7acec074f812c8a authorGabe McArthur <gabemc@crowdflower.com> dateSat 26 Oct 2013, 12:32 (13 years ago) parent8668722
2 files changed+37−55
Data/Aeson/TH.hs+2−46 Data/Aeson/Types/Internal.hs+35−9
Data/Aeson/TH.hs+2−46
@@ -70,11 +70,6 @@ module Data.Aeson.TH
7070
7171 , mkToJSON
7272 , mkParseJSON
73−
74− -- * For the oft-conversion of CamelCase to underscore_case in JSON
75− , camelToUnderscore
76− , camelToUnderscoreDrop
77− , camelToUnderscoreDropChecked
7873 ) where
7974
8075--------------------------------------------------------------------------------
@@ -95,7 +90,6 @@ import Data.Aeson.Types ( Value(..), Parser
9590-- from base:
9691import Control.Applicative ( pure, (<$>), (<*>) )
9792import Control.Monad ( return, mapM, liftM2, fail )
98−import Data.Char ( isUpper, toLower)
9993import Data.Bool ( Bool(False, True), otherwise, (&&) )
10094import Data.Eq ( (==) )
10195import Data.Function ( ($), (.) )
@@ -103,10 +97,10 @@ import Data.Functor ( fmap )
10397import Data.Int ( Int )
10498import Data.Either ( Either(Left, Right) )
10599import Data.List ( (++), foldl, foldl', intercalate
106− , length, map, zip, genericLength, all, partition, drop
100+ , length, map, zip, genericLength, all, partition
107101 )
108102import Data.Maybe ( Maybe(Nothing, Just), catMaybes )
109−import Prelude ( String, (-), Integer, fromIntegral, error, const )
103+import Prelude ( String, (-), Integer, fromIntegral, error )
110104import Text.Printf ( printf )
111105import Text.Show ( show )
112106#if __GLASGOW_HASKELL__ < 700
@@ -878,41 +872,3 @@ valueConName (String _) = "String"
878872valueConName (Number _) = "Number"
879873valueConName (Bool _) = "Boolean"
880874valueConName 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
3535 , SumEncoding(..)
3636 , defaultOptions
3737 , defaultTaggedObject
38+
39+ -- Used for changing CamelCase names into something else.
40+ , camelTo
3841 ) where
3942
4043import Control.Applicative
4144import Control.Monad
42−import Control.DeepSeq (NFData(..))
43−import Data.Attoparsec.Char8 (Number(..))
44−import Data.Hashable (Hashable(..))
45−import Data.HashMap.Strict (HashMap)
46−import Data.Monoid (Monoid(..))
47−import Data.String (IsString(..))
48−import Data.Text (Text, pack)
49−import Data.Typeable (Typeable)
50−import Data.Vector (Vector)
45+import Control.DeepSeq ( NFData(..) )
46+import Data.Attoparsec.Char8 ( Number(..) )
47+import Data.Char ( isUpper, toLower )
48+import Data.Hashable ( Hashable(..) )
49+import Data.HashMap.Strict ( HashMap )
50+import Data.Monoid ( Monoid(..) )
51+import Data.String ( IsString(..) )
52+import Data.Text ( Text, pack )
53+import Data.Typeable ( Typeable )
54+import Data.Vector ( Vector )
5155import qualified Data.HashMap.Strict as H
5256import qualified Data.Vector as V
5357
@@ -328,3 +332,25 @@ defaultTaggedObject = TaggedObject
328332 { tagFieldName = "tag"
329333 , contentsFieldName = "contents"
330334 }
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+