2014-11-02 6 views
4

저는 명령형 프로그래밍 배경에서 오는 하스켈을 처음 사용합니다. 나는 "하스켈 방식"으로 JSON에 객체를 직렬화 할 수 있기를 원하지만, 아직 어떻게해야할지 잘 모르겠다.하스켈 - 동적 JSON 필드로 유형을 모델링하는 방법은 무엇입니까?

나는 JSON에 대해 약간 말한 Chapter 5 of RealWorldHaskell을 읽고 Aeson과 놀았습니다. 에서 매우 기본적인 JSON 문자열을 만들 수 있다는 점에 날 잡았어

: 나는 또한 같은 하스켈로 작성된 몇 JSON의 API 라이브러리를 살펴 보았다 객체 (this blog post에 또한 감사) :

{-# LANGUAGE OverloadedStrings, DeriveGeneriC#-} 

import Data.Aeson 
import GHC.Generics 

data User = User { 
    email :: String, 
    name :: String 
} deriving (Show, Generic) 

instance ToJSON User 

main = do 
    let user = User "[email protected]" "Hello World" 
    let json = encode user 
    putStrLn $ show json 

출력 할 것이다 :

,536를
"{\"email\":\"[email protected]",\"name\":\"Hello World\"}" 

목표는 임의의 필드를 가질 수있는 User 인스턴스에 다른 필드를 추가하는 것입니다. Facebook Graph API에는 data이라는 필드가 있습니다.이 필드는 원하는 모든 속성을 가진 JSON 객체입니다. data 필드가있는 동안,

POST api.facebook.com/actions 
{ 
    "name": "read", 
    "object": "book", 
    "data": { 
    "favoriteChapter": 10, 
    "hardcover": true 
    } 
} 

첫 번째 두 개의 필드, nameobject 유형 String 있습니다 : 예를 들어, 당신은 페이스 북의 API (의사, 정확하게 페이스 북 API에 익숙하지 않은)이 같은 요청을 할 수 있습니다 임의의 프로퍼티의 맵

위의 User 모델에서 달성 할 수있는 "하스켈 방식"은 무엇입니까?

data User = User { 
    email :: String, 
    name :: String, 
    data :: CustomData 
} deriving (Show, Generic) 

data CustomData = CustomData { 
    favoriteColor :: String 
} 

을하지만 내가 찾고 있어요 확실히 어떤되지 않습니다 :

나는 간단한 경우 작업을 수행하는 방법을 이해할 수있다.

{ 
    "email": "", 
    "name": "", 
    "data": { 
    "favoriteColor": "" 
    } 
} 

문제는 당신이 그래서 당신은 임의의 필드를 가질 수 있습니다 다음 번에 그 User 유형을 정의해야하고, 어떻게해야합니까입니다 : 즉, User 유형, 직렬화 된 JSON에, 항상이 모양을 의미 정적 유형 지정 (또는 형식의 세부 사항에 익숙하지 않은 익숙하지 않은 곳이면 무엇이든간에)으로부터 여전히 이익을 얻으면서 data 속성에 첨부됩니다.

+1

'어쩌면'과 '모두'를보고 싶습니다. –

+0

http://hackage.haskell.org/package/aeson-0.6.1.0/docs/Data-Aeson.html#g:8 (객체 별칭) –

+1

임의의 데이터 유형으로 정적 유형 지정을 즐기는 것도 불가능합니다. 어떻게 동적 필드를 정적으로 분석 할 수 있습니까? '임의의 데이터'로 기대할 수있는 최선의 방법은 런타임에서 오류가 발생하거나 런타임에 올바르게 가져 오는 것입니다 (예를 들어 Either와 아마도를 통해 가능하지만 여전히 임의적이지는 않습니다). –

답변

4

임의의 데이터가 무엇을 의미하는지에 따라 다릅니다. 나는 "데이터가 임의의 문서 유형을 포함하고있다"라는 합리적이고 사소한 정의라고 생각하는 것을 추출하여 몇 가지 가능성을 보여줄 것입니다.

먼저 저는 과거의 내 블로그 게시물을 가리키고 있습니다. 이것은 구조 나 성격이 다른 문서를 구문 분석하는 방법을 보여줍니다. 여기 예를 들어 기존 : 난 당신이 더 높은 kinded 유형의 사용과 파라미터 화 구조를 정의 보여주지

data CustomData = NotesData Text | UserAge Int deriving (Show, Generic) 
newtype Email = Email Text deriving (Show, Generic) 
newtype Name = Name Text deriving (Show, Generic) 

data User = User { 
    email :: Email, 
    name :: Name, 
    data :: CustomData 
} deriving (Show, Generic) 

다음 : http://bitemyapp.com/posts/2014-04-17-parsing-nondeterministic-data-with-aeson-and-sum-types.html

하면 데이터 유형에 적용,이 같은 것을 볼 수 있었다.여기 예를 들어 기존 : http://bitemyapp.com/posts/2014-04-11-aeson-and-user-created-types.html

newtype Email = Email Text deriving (Show, Generic) 
newtype Name = Name Text deriving (Show, Generic) 

-- 'a' needs to implement ToJSON/FromJSON as appropriate 
data User a = User { 
    email :: Email, 
    name :: Name, 
    data :: a 
} deriving (Show, Generic) 

을 위의 코드와 함께 우리는 매개 변수 data을했고 User 높은 kinded 유형을했다. 이제 User은 유형 인수의 유형에 의해 매개 변수화 된 구조로되어 있습니다. data 필드는 이제 User CustomData, User Text 문자열 또는 User Int 문자열과 같은 문서가 될 수 있습니다. 아마도 Int/String이 아닌 의미 상 의미있는 유형을 원할 것입니다. 필요에 따라 newtype을 사용하십시오.

다른 방법으로 (Double, Double)로 인코딩 할 데이터 유형에 구조와 의미를 제공하는 방법에 대한 다소 실용적인 예제는 https://github.com/NICTA/coordinate을 참조하십시오.

적절하다고 생각되는 경우 이러한 접근 방식을 결합 할 수 있습니다. 이는 부분적으로는 유형을 둘러싸는 문서에 대한 유형 인수에서 특정 단일의 가능성을 표현할 수 있는지 여부에 달려 있습니다.

나는지도 원리가 가능한 범위에 대한 유형을 통해 잘못된 데이터가 표현할 수없는 수 있도록하는 것입니다 JSON 처리 코드와 https://github.com/bitemyapp/bloodhound

에서 내 라이브러리에 데이터를 구조화하는 방법의 예의 톤이있다. 유형만으로는 데이터의 유효성을 검사 할 수없는 경우 "스마트 생성자"사용을 고려하십시오.

여기에 스마트 생성자에 대한 자세한 참조 : https://www.haskell.org/haskellwiki/Smart_constructors

+0

JSON 구문 분석 코드가 "톤"인 경우 아마 FromJSON의 자동 파생을 사용하는 것을 고려할 것인가? 또는 json-autotype과 같은 샘플 JSON 응답에서 Haskell 유형을 자동으로 생성합니까? –

+1

@MichalGajda 일반적으로 제네릭을 사용하여 인스턴스를 생성 한 다음 인스턴스를 터미널에 덤프하고 파일에 복사하여 동작을 명시 적으로 복사합니다. 사소한 것이라면 일반적으로 파생 된 인스턴스를 남겨 둘 것입니다. 환경 설정. – bitemyapp

+0

훌륭한 솔루션이지만 '데이터'필드가 존재한다고 가정합니다. OP는 필드가 존재할 수도 있고 존재하지 않을 수도있는 시나리오에 대해 질문했습니다. 이 사건을 어떻게 해결할 수 있습니까? – dopatraman

2

당신이 정말로 아이손의 FromJSON 클래스와 완전히 임의의 JSON 하부 구조를 수용하고 싶었을, 당신이 아이손의 제네릭 필드 사용자 :: 값을 만드는 것이 좋을 걸 JSON 값을 입력하십시오. 나중에이 JSON 값의 가능한 유형을 찾으면 FromJSON을 사용하여 다시 변환 할 수 있지만 처음에는 거기에있는 내용이 모두 저장됩니다.

+0

값은 데이터 구조에 대한 실제 정보를 제공하지 않습니다. 나는 당신이 통제하는 구조를 가진 실제적인 비즈니스 로직 래핑 데이터를 위해 이것을 추천하지 않을 것이다. 당신은 하스켈의 타입 시스템의 이점을 잃어 버리게됩니다. – bitemyapp