Haskell/Aeson:将JSON作为一个对象输出

3
我有以下Haskell代码,它将一个User数据类型的列表编码为JSON格式并将其输出到标准输出:
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Data.Aeson
import Data.Text
import qualified Data.ByteString.Lazy.Char8 as B

data User = User
    { id :: String
    , name :: String
    , address :: String
    } deriving (Show)

instance ToJSON User where
    toJSON (User id name address) = object
        [ pack id .= object
            [ "name" .= name
            , "address" .= address
            ]
        ]

users :: [User]
users = [ User "user 1" "name of user 1" "address of user 1"
        , User "user 2" "name of user 2" "address of user 2"
        ] 

main :: IO ()
main = B.putStrLn $ encode users

目前,该代码会产生以下输出结果:
[
  {
    "user 1": {
      "address": "address of user 1",
      "name": "name of user 1"
    }
  },
  {
    "user 2": {
      "address": "address of user 2",
      "name": "name of user 2"
    }
  }
]

然而,我希望输出以下JSON结构(连接内部两个对象):
{
  "user 1": {
    "name": "name of user 1",
    "address": "address of user 1"
  },
  "user 2": {
    "name": "name of user 2",
    "address": "address of user 2"
  }
}

我需要如何更改toJSON函数以打印所需的编码JSON?


我不太擅长Haskell,但你能否去掉包裹内部对象的外部对象? - MiltoxBeyond
不是很好办,因为我不能仅仅摆脱外部对象,因为有两个内部对象而不仅仅是一个。首先必须合并内部对象。 - watain
1
问题中有错别字吗?我在期望的输出中看到了"lastname": "name of user 2",但是问题中没有其他地方出现"lastname" - Dave Compton
@DaveCompton,是的,抱歉,我已经纠正了拼写错误。 - watain
1个回答

2
如何更改toJSON以打印所需的编码JSON?
无法更改User的toJSON以打印所需的编码JSON。问题不在User编码中,而在列表编码中。简单列表只编码为JSON数组。而JSON数组没有值(因此您不能拥有["user 1":{...}],因此每个对象都包装在{}中)。这个问题可以用不同的方法解决。其中最简单的解决方案之一是为User列表编写自定义编码器。以下是其外观:
import qualified Data.HashMap.Strict as HM

usersEncode :: [User] -> Object
usersEncode = HM.unions . map (\(Object user) -> user) . map toJSON

然后在main函数中,您可以这样调用它:

main = B.putStrLn $ encode $ usersEncode users

它可以为您提供所需的输出。

窍门在于,aeson 将对象存储为从 TextValueHashMap。而 HashMap 会被编码为 JSON 对象。因此,给定解决方案的思路是将每个用户转换为单例 HashMap,然后合并所有 HashMap

注意:它将从列表中删除具有重复 userId 的用户。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接