Skip to content

Commit 45d8027

Browse files
authored
[haskell-servant][haskell-yesod] Use table-based conversion for field name conversion (#16232)
* [haskell-servant][haskell-yesod] use table-based conversion for field name conversion Current fieldLabelModifier implementation always produces uncapitalize name, but it is inappropriate if the original JSON field name begins with a capital letter. * [haskell-servant][haskell-yesod] regenerate samples
1 parent 81c398e commit 45d8027

File tree

6 files changed

+229
-185
lines changed

6 files changed

+229
-185
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@ public CodegenModel fromModel(String name, Schema mod) {
658658
String prefix = camelize(model.classname, LOWERCASE_FIRST_LETTER);
659659
for (CodegenProperty prop : model.vars) {
660660
prop.name = toVarName(prefix + camelize(prop.name));
661+
prop.vendorExtensions.put("x-base-name-string-literal", "\"" + escapeText(prop.getBaseName()) + "\"");
661662
}
662663

663664
// Create newtypes for things with non-object types
@@ -668,8 +669,6 @@ public CodegenModel fromModel(String name, Schema mod) {
668669
model.vendorExtensions.put("x-custom-newtype", newtype);
669670
}
670671

671-
// Provide the prefix as a vendor extension, so that it can be used in the ToJSON and FromJSON instances.
672-
model.vendorExtensions.put("x-prefix", prefix);
673672
model.vendorExtensions.put("x-data", dataOrNewtype);
674673

675674
return model;

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellYesodServerCodegen.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ public CodegenModel fromModel(String name, Schema mod) {
552552
String prefix = camelize(model.classname, LOWERCASE_FIRST_LETTER);
553553
for (CodegenProperty prop : model.vars) {
554554
prop.name = toVarName(prefix + camelize(prop.name));
555+
prop.vendorExtensions.put("x-base-name-string-literal", "\"" + escapeText(prop.getBaseName()) + "\"");
555556
}
556557

557558
// Create newtypes for things with non-object types
@@ -562,8 +563,6 @@ public CodegenModel fromModel(String name, Schema mod) {
562563
model.vendorExtensions.put("x-custom-newtype", newtype);
563564
}
564565

565-
// Provide the prefix as a vendor extension, so that it can be used in the ToJSON and FromJSON instances.
566-
model.vendorExtensions.put("x-prefix", prefix);
567566
model.vendorExtensions.put("x-data", dataOrNewtype);
568567

569568
return model;

modules/openapi-generator/src/main/resources/haskell-servant/Types.mustache

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module {{title}}.Types (
1313

1414
import Data.Data (Data)
1515
import Data.UUID (UUID)
16-
import Data.List (stripPrefix)
16+
import Data.List (lookup)
1717
import Data.Maybe (fromMaybe)
1818
import Data.Aeson (Value, FromJSON(..), ToJSON(..), genericToJSON, genericParseJSON)
1919
import Data.Aeson.Types (Options(..), defaultOptions)
@@ -26,7 +26,6 @@ import qualified Data.Char as Char
2626
import qualified Data.Text as T
2727
import qualified Data.Map as Map
2828
import GHC.Generics (Generic)
29-
import Data.Function ((&))
3029
{{#imports}}import {{import}}
3130
{{/imports}}
3231

@@ -42,16 +41,28 @@ import Data.Function ((&))
4241
} deriving (Show, Eq, Generic, Data)
4342

4443
instance FromJSON {{classname}} where
45-
parseJSON = genericParseJSON (removeFieldLabelPrefix "{{vendorExtensions.x-prefix}}")
44+
parseJSON = genericParseJSON options{{classname}}
4645
instance ToJSON {{classname}} where
47-
toJSON = genericToJSON (removeFieldLabelPrefix "{{vendorExtensions.x-prefix}}")
46+
toJSON = genericToJSON options{{classname}}
4847
{{#generateToSchema}}
4948
instance ToSchema {{classname}} where
5049
declareNamedSchema = Swagger.genericDeclareNamedSchema
5150
$ Swagger.fromAesonOptions
52-
$ removeFieldLabelPrefix "{{vendorExtensions.x-prefix}}"
51+
$ options{{classname}}
5352
{{/generateToSchema}}
5453

54+
options{{classname}} :: Options
55+
options{{classname}} =
56+
defaultOptions
57+
{ omitNothingFields = True
58+
, fieldLabelModifier = \s -> fromMaybe ("did not find JSON field name for " ++ show s) $ lookup s table
59+
}
60+
where
61+
table =
62+
[ {{#vars}}("{{& name}}", {{& vendorExtensions.x-base-name-string-literal}}){{^-last}}
63+
, {{/-last}}{{/vars}}
64+
]
65+
5566
{{/parent}}
5667
{{#parent}}
5768
newtype {{classname}} = {{classname}} { un{{classname}} :: {{parent}} }
@@ -63,23 +74,3 @@ newtype {{classname}} = {{classname}} {{vendorExtensions.x-custom-newtype}} deri
6374
{{/vendorExtensions.x-custom-newtype}}
6475
{{/model}}
6576
{{/models}}
66-
67-
uncapitalize :: String -> String
68-
uncapitalize (first:rest) = Char.toLower first : rest
69-
uncapitalize [] = []
70-
71-
-- | Remove a field label prefix during JSON parsing.
72-
-- Also perform any replacements for special characters.
73-
removeFieldLabelPrefix :: String -> Options
74-
removeFieldLabelPrefix prefix =
75-
defaultOptions
76-
{ omitNothingFields = True
77-
, fieldLabelModifier = uncapitalize . replaceSpecialChars . fromMaybe (error ("did not find prefix " ++ prefix)) . stripPrefix prefix
78-
}
79-
where
80-
replaceSpecialChars field = foldl (&) field (map mkCharReplacement specialChars)
81-
specialChars =
82-
[ {{#specialCharReplacements}}("{{&char}}", "{{&replacement}}"){{^-last}}
83-
, {{/-last}}{{/specialCharReplacements}}
84-
]
85-
mkCharReplacement (replaceStr, searchStr) = T.unpack . T.replace (T.pack searchStr) (T.pack replaceStr) . T.pack

modules/openapi-generator/src/main/resources/haskell-yesod/src/API/Types.mustache

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ module {{apiModuleName}}.Types (
1313

1414
import ClassyPrelude.Yesod
1515
import Data.Foldable (foldl)
16+
import qualified Data.List as List
1617
import Data.Maybe (fromMaybe)
1718
import Data.Aeson (Value, FromJSON(..), ToJSON(..), genericToJSON, genericParseJSON)
1819
import Data.Aeson.Types (Options(..), defaultOptions)
1920
import qualified Data.Char as Char
2021
import qualified Data.Text as T
2122
import qualified Data.Map as Map
2223
import GHC.Generics (Generic)
23-
import Data.Function ((&))
2424
{{#imports}}import {{import}}
2525
{{/imports}}
2626

@@ -36,9 +36,21 @@ import Data.Function ((&))
3636
} deriving (Show, Eq, Generic)
3737

3838
instance FromJSON {{classname}} where
39-
parseJSON = genericParseJSON (removeFieldLabelPrefix "{{vendorExtensions.x-prefix}}")
39+
parseJSON = genericParseJSON options{{classname}}
4040
instance ToJSON {{classname}} where
41-
toJSON = genericToJSON (removeFieldLabelPrefix "{{vendorExtensions.x-prefix}}")
41+
toJSON = genericToJSON options{{classname}}
42+
43+
options{{classname}} :: Options
44+
options{{classname}} =
45+
defaultOptions
46+
{ omitNothingFields = True
47+
, fieldLabelModifier = \s -> fromMaybe ("did not find JSON field name for " ++ show s) $ List.lookup s table
48+
}
49+
where
50+
table =
51+
[ {{#vars}}("{{& name}}", {{& vendorExtensions.x-base-name-string-literal}}){{^-last}}
52+
, {{/-last}}{{/vars}}
53+
]
4254

4355
{{/parent}}
4456
{{#parent}}
@@ -51,23 +63,3 @@ newtype {{classname}} = {{classname}} {{vendorExtensions.x-custom-newtype}} deri
5163
{{/vendorExtensions.x-custom-newtype}}
5264
{{/model}}
5365
{{/models}}
54-
55-
uncapitalize :: String -> String
56-
uncapitalize (c : cs) = Char.toLower c : cs
57-
uncapitalize [] = []
58-
59-
-- | Remove a field label prefix during JSON parsing.
60-
-- Also perform any replacements for special characters.
61-
removeFieldLabelPrefix :: String -> Options
62-
removeFieldLabelPrefix prefix =
63-
defaultOptions
64-
{ omitNothingFields = True
65-
, fieldLabelModifier = uncapitalize . replaceSpecialChars . fromMaybe (error ("did not find prefix " ++ prefix)) . stripPrefix prefix
66-
}
67-
where
68-
replaceSpecialChars field = foldl (&) field (map mkCharReplacement specialChars)
69-
specialChars =
70-
[ {{#specialCharReplacements}}("{{&char}}", "{{&replacement}}"){{^-last}}
71-
, {{/-last}}{{/specialCharReplacements}}
72-
]
73-
mkCharReplacement (replaceStr, searchStr) = T.unpack . T.replace (T.pack searchStr) (T.pack replaceStr) . T.pack

0 commit comments

Comments
 (0)