漂亮打印 JavaScript 个对象
Pretty-printing JavaScript objects
我选择围绕 JSON 对象和 wl-pprint-annotated (here is the paper behind that library) 来集中这个问题,因为它们使拥有 MVCE 变得容易,但我的问题实际上不是关于漂亮打印 只是 JSON 个对象,我可以灵活选择我使用的漂亮打印库。
考虑以下简化的 JavaScript 对象数据类型:
data Object = Object [(String, Object)]
| String String
如何定义一个漂亮的打印函数,以通常的方式将其输出包装成多行?我的意思是:漂亮的打印输出应该,尽可能放在一条线上。如果不可能,我希望最外面的对象在内部对象之前开始添加换行符。
这是一次使用 wl-pprint-annotated 的尝试:
{-# LANGUAGE OverloadedString #-}
import Text.PrettyPrint.Annotated.WL
prettyObject :: Object -> Doc a
prettyObject (String str) = "\"" <> text str <> "\""
prettyObject (Object fields) = Union ("{" <+> hsep fields' <+> "}")
("{" <#> indent 2 (vsep fields') <#> "}")
where
fields' :: [Doc a]
fields' = punctuate "," [ text key <> ":" <+> prettyObject val
| (key,val) <- fields ]
现在,一些测试用例。
ghci> o1 = Object [("key1", String "val1")]
ghci> o2 = Object [("key2", String "val2"), ("looooooooooong key3", String "loooooooooooong val3"),("key4", String "val4")]
ghci> o3 = Object [("key5", String "val5"), ("key6", o2), ("key7", String "val7")]
ghci> prettyObject o1
{ key1: "val1" }
ghci> prettyObject o2
{
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
}
ghci> prettyObject o3
{ key5: { key1: "val1" }, key6: {
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
}, key7: "val7" }
我希望最后的输出是
{
key5: { key1: "val1" },
key6: {
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
},
key7: "val7"
}
我正在寻找一种以某种方式适合 Haskell 中现有漂亮打印库之一的解决方案(实际上,我正在漂亮打印 much不仅仅是 JSON).
的一个子集
我 不是 寻找定义 prettyObject :: Object -> String
的解决方案 - 这种方法的全部意义在于 Doc
的渲染取决于它在漂亮印刷的大图中的位置。
您正在使用的漂亮打印库已经可以做到这一点; (你刚刚告诉它做不同的事情!)一般来说,这个漂亮的打印机家族(WL)可以很好地处理这种情况。
请注意您的 Union
的位置:
prettyObject (Object fields) = Union <one line> <many line>
在您的文本中您逻辑上选择中断的位置,即键值对的开头,您的 [=18= 中没有 Union
] 结构体。选择是在 {..}
封闭块开始的地方进行的;如果你仔细检查输出,这正是它给你的:
{ key5: { key1: "val1" }, key6: { ----- line break here
key2: "val2",
您需要一个函数来实现所需的键值对逻辑:
indent' k x = flatAlt (indent k x) (flatten x)
prettyKVPair (k,v) = indent' 2 $ text k <> ":" <+> pretty v
indent'
类似于 indent
,但提供了一个不缩进的显式替代方案。 flatAlt
提供了一个替代方案,当文本被展平时使用,您的文本将被展平(您可能已经猜到了)flatten
。您还需要相应地重新构造 prettyObject
:
prettyObject :: Object -> Doc a
prettyObject (Object fields) = sep $ "{" : fields' ++ [ "}" ] where
fields' = punctuate "," $ map prettyKVPair fields
...
请注意,没有明确的 Union
,而是 sep = group . vsep
和 group = \x -> Union (flatten x) x
。您现在有一个联合对应于关于您在何处展平文本的逻辑选择。
结果:
>pretty o1
{ key1: "val1" }
>pretty o2
{
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
}
>pretty o3
{
key5: "val5",
key6: {
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
},
key7: "val7"
}
针对评论中的问题,提供平面替代的方法是使用flatAlt
,当然!这里唯一的问题是你想对列表的单个元素(最后一个)执行此操作 - 但这是列表的问题,而不是 Doc
。随意使用 Data.Sequence
或任何其他 Traversable
,大多数 'list-like' 函数(如 punctuate
)都可以使用它,如果这是一个你需要很多的操作。
flattenedOf a b = flatAlt a (flatten b) # useful combinator
trailingSep _ [] = []
trailingSep s xs = as ++ [ (a <> s) `flattenedOf` a ]
where as = init xs; a = last xs
...
prettyObject (Object fields) = <unchanged> where
fields' = trailingSep "," $ <unchanged>
我选择围绕 JSON 对象和 wl-pprint-annotated (here is the paper behind that library) 来集中这个问题,因为它们使拥有 MVCE 变得容易,但我的问题实际上不是关于漂亮打印 只是 JSON 个对象,我可以灵活选择我使用的漂亮打印库。
考虑以下简化的 JavaScript 对象数据类型:
data Object = Object [(String, Object)]
| String String
如何定义一个漂亮的打印函数,以通常的方式将其输出包装成多行?我的意思是:漂亮的打印输出应该,尽可能放在一条线上。如果不可能,我希望最外面的对象在内部对象之前开始添加换行符。
这是一次使用 wl-pprint-annotated 的尝试:
{-# LANGUAGE OverloadedString #-}
import Text.PrettyPrint.Annotated.WL
prettyObject :: Object -> Doc a
prettyObject (String str) = "\"" <> text str <> "\""
prettyObject (Object fields) = Union ("{" <+> hsep fields' <+> "}")
("{" <#> indent 2 (vsep fields') <#> "}")
where
fields' :: [Doc a]
fields' = punctuate "," [ text key <> ":" <+> prettyObject val
| (key,val) <- fields ]
现在,一些测试用例。
ghci> o1 = Object [("key1", String "val1")]
ghci> o2 = Object [("key2", String "val2"), ("looooooooooong key3", String "loooooooooooong val3"),("key4", String "val4")]
ghci> o3 = Object [("key5", String "val5"), ("key6", o2), ("key7", String "val7")]
ghci> prettyObject o1
{ key1: "val1" }
ghci> prettyObject o2
{
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
}
ghci> prettyObject o3
{ key5: { key1: "val1" }, key6: {
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
}, key7: "val7" }
我希望最后的输出是
{
key5: { key1: "val1" },
key6: {
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
},
key7: "val7"
}
我正在寻找一种以某种方式适合 Haskell 中现有漂亮打印库之一的解决方案(实际上,我正在漂亮打印 much不仅仅是 JSON).
的一个子集我 不是 寻找定义 prettyObject :: Object -> String
的解决方案 - 这种方法的全部意义在于 Doc
的渲染取决于它在漂亮印刷的大图中的位置。
您正在使用的漂亮打印库已经可以做到这一点; (你刚刚告诉它做不同的事情!)一般来说,这个漂亮的打印机家族(WL)可以很好地处理这种情况。
请注意您的 Union
的位置:
prettyObject (Object fields) = Union <one line> <many line>
在您的文本中您逻辑上选择中断的位置,即键值对的开头,您的 [=18= 中没有 Union
] 结构体。选择是在 {..}
封闭块开始的地方进行的;如果你仔细检查输出,这正是它给你的:
{ key5: { key1: "val1" }, key6: { ----- line break here
key2: "val2",
您需要一个函数来实现所需的键值对逻辑:
indent' k x = flatAlt (indent k x) (flatten x)
prettyKVPair (k,v) = indent' 2 $ text k <> ":" <+> pretty v
indent'
类似于 indent
,但提供了一个不缩进的显式替代方案。 flatAlt
提供了一个替代方案,当文本被展平时使用,您的文本将被展平(您可能已经猜到了)flatten
。您还需要相应地重新构造 prettyObject
:
prettyObject :: Object -> Doc a
prettyObject (Object fields) = sep $ "{" : fields' ++ [ "}" ] where
fields' = punctuate "," $ map prettyKVPair fields
...
请注意,没有明确的 Union
,而是 sep = group . vsep
和 group = \x -> Union (flatten x) x
。您现在有一个联合对应于关于您在何处展平文本的逻辑选择。
结果:
>pretty o1
{ key1: "val1" }
>pretty o2
{
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
}
>pretty o3
{
key5: "val5",
key6: {
key2: "val2",
looooooooooong key3: "loooooooooooong val3",
key4: "val4"
},
key7: "val7"
}
针对评论中的问题,提供平面替代的方法是使用flatAlt
,当然!这里唯一的问题是你想对列表的单个元素(最后一个)执行此操作 - 但这是列表的问题,而不是 Doc
。随意使用 Data.Sequence
或任何其他 Traversable
,大多数 'list-like' 函数(如 punctuate
)都可以使用它,如果这是一个你需要很多的操作。
flattenedOf a b = flatAlt a (flatten b) # useful combinator
trailingSep _ [] = []
trailingSep s xs = as ++ [ (a <> s) `flattenedOf` a ]
where as = init xs; a = last xs
...
prettyObject (Object fields) = <unchanged> where
fields' = trailingSep "," $ <unchanged>