2012-12-12 3 views
6

Ich schreibe einen data Datensatz zu Marshall ein JIRAJSON Objekt. Das Problem ist, mehrere Objekte haben die gleichen Bezeichnungen für Name/Wert-Paare. Zum Beispiel:Wie man mit Haskell Namensraum fertig wird?

(von Locke zurück und formatiert)

{"expand":"schema,names" 
,"startAt":0 
,"maxResults":2 
,"total":74 
,"issues":[ 
      {"expand":"editmeta,renderedFields,transitions,changelog,operations" 
      ,"id":"183614" 
      ,"self":"https://10.64.16.44/rest/api/latest/issue/183614" 
      ,"key":"BNAP-339" 
      ,"fields":{"versions":[ 
            {"self":"https://10.64.16.44/rest/api/2/version/28240" 
            ,"id":"28240" 
            ,"name":"2012-12-07" 
            ,"archived":false 
            ,"released":false 
            } 
           ] 
        ,"status":{"self":"https://10.64.16.44/rest/api/2/status/1" 
           ,"description":"The issue is open and ready for the assignee to start work on it." 
           ,"iconUrl":"https://10.64.16.44/images/icons/status_open.gif" 
           ,"name":"Open" 
           ,"id":"1" 
           } 
        ,"description":"Do Re Mi Fa" 
        ,"resolution":null 
        } 
      } 
      ] 

, wenn ich die problematischen entsprechenden Haskell data Aufzeichnungen Konstrukt erhalte ich:

data Issue = Issue {expand :: String 
        ,id :: String 
        ,self :: String 
        ,key :: String 
        ,fields :: Fields 
        } deriving Generic 


data Version = Version {self :: String 
         ,id :: String 
         ,name :: String 
         ,archived :: Bool 
         ,released :: Bool 
         } deriving Generic 

und 'id' und 'Selbst' kollidieren . Es ist mir aufgefallen, dass ich das lösen könnte, indem ich einfach die Namen in den Datensätzen ändere und sie mit einer manuell erzeugten FromJSON Instanz behebe. Jede alternative Lösung wäre willkommen.

Antwort

10

Ich löse dies in Protokollpuffern, indem Sie Dinge wie Issue und Version in separaten Dateien in der gleichen Hierarchie setzen.

Haskell verwendet nur separate Module, um Namespaces zu steuern, also ist dies die orthodoxe Lösung.

Viele viel schicker: Nutzungsart Klassen Namen zu definieren:

class Has'self a b | a -> bwhere 
    get'self :: a -> b 
    set'self :: b -> a -> b 

instance Has'self Issue String where ... 
instance Has'self Version String where .... 

EDIT: Die Kommentare mich unten erinnern ausführliche Beratung. Benutze keine Sich selbst wie Lösungen - diejenigen, die diesen Weg gegangen sind, berichten, dass es hässlich wird. Ich kann für den Pfad einzelner Module bürgen.

PS: Vielleicht können Sie die lens Bibliothek für Ihre Felder verwenden!

+7

Beachten Sie, dass 'HasFoobar' Stil typeclasses fast immer eine schreckliche Idee, wenn es sauber zu schreiben kommt, gut strukturierten Haskell-Code. Wenn jedoch versucht wird, die Struktur von Nicht-Haskell-Code für Interop-Zwecke abzugleichen, gibt es möglicherweise keinen besseren Ansatz, wenn die andere Seite stark auf überladene Funktionen und/oder Subtyphierarchien angewiesen ist. –

+0

Ich aktualisiere den Teil, in dem Sie separate Dateien empfohlen haben. Typenklassen sorgen für eine schlechte Namespacing-Lösung, da sie für die Benutzer sehr schwierig sind, sich über die Typen zu verständigen und im Stillen fehlschlagen und das Falsche tun, wenn sie auf den falschen Typ angewendet werden. –

3

Eine andere Alternative, die funktionieren sollte, besteht darin, einen einzelnen Datentyp zu verwenden, der die unterschiedlichen Datensätze enthält. Zum Beispiel ist der folgende Datentyp der Lage Ihren Wert ohne Feldkonflikte darstellen:

import Prelude hiding (id) 

data JIRA = JIRA 
    { expand :: String 
    , startAt :: Int 
    , maxResults :: Int 
    , total :: Int 
    , issues :: [JIRA] 
    } | Issue 
    { expand :: String 
    , id :: Int 
    , self :: String 
    , key :: String 
    , fields :: JIRA 
    } | Field 
    { versions :: [JIRA] 
    , status :: JIRA 
    , description :: String 
    , resolution :: Maybe String 
    } | Version 
    { self :: String 
    , id :: Int 
    , name :: String 
    , archived :: Bool 
    , released :: Bool 
    } | Status 
    { self :: String 
    , description :: String 
    , iconUrl :: String 
    , name :: String 
    , id :: Int 
    } 


yourExample = JIRA 
    { expand = "schema, names" 
    , startAt = 0 
    , maxResults = 2 
    , total = 74 
    , issues = [ Issue 
       { expand = "editmeta, etc..." 
       , id = 12345 
       , self = "https://xyz" 
       , key = "BLAH" 
       , fields = Field 
          { versions = [ Version 
              { self = "https://foobar" 
              , id = 1234 
              , name = "quux" 
              , archived = False 
              , released = False 
              } 
             ] 
          , status = Status 
            { self = "https://etc" 
            , description = "issue" 
            , iconUrl = "https://iconurl" 
            , name = "open" 
            , id = 1 
            } 
          , description = "another description" 
          , resolution = Nothing 
          } 
       } 
       ] 
    } 
+0

Beachten Sie, dass das obige nur funktioniert, wenn alle 'id' vom selben Typ sind ('Int') und alle' self' vom selben Typ sind ('String'), usw. –