I'm trying to write an xpickle which serialises the value constructors of some type into XML attribute values of a specific attribute and de-serialises the XML attribute values back into value constructors of the type.
I have the follow data:
module Main where
import Text.XML.HXT.Core
newtype Things = Things [Thing]
data Thing = Thing (Maybe Property)
data Property = A | B
someThings :: Things
someThings = Things [ Thing (Just A)
, Thing Nothing
, Thing (Just B)
]
And I'd like to serialise this into something like:
<things>
<thing property="a" />
<thing />
<thing property="b" />
</things>
Here's the approach I'm taking:
instance XmlPickler Things where
xpickle = xpWrap ( \things -> Things things , \(Things things) -> things ) $
xpElem "things" $
xpThings
xpThings :: PU [Thing]
xpThings = xpList xpickle
instance XmlPickler Thing where
xpickle = xpElem "thing" $
xpWrap ( \p -> Thing p , \(Thing p) -> p ) $
xpProperty
xpProperty :: PU (Maybe Property)
xpProperty = xpOption $ xpAttr "property" xpPropertyValue
xpPropertyValue :: PU Property
xpPropertyValue = xpAlt tag ps
where
tag A = 1
tag B = 2
ps = [ xpTextAttr "a"
, xpTextAttr "b"
]
main :: IO ()
main = do
putStrLn $ showPickled [ withIndent yes ] someThings
return ()
Here, xpProperty
creates or reads an @property
attribute and then uses xpPropertyValue
to work out the value. xpPropertyValue
determines the value depending on the value constructor of the value: A
gives "a"
and B
gives "b"
and the values are constructed using the xpTextAttr
function. The problem here is that xpTextAttr
is String -> PU String
and I'm trying to use it where I need a PU Property
. But I can't work out an alternative way of generating a PU Property
value that's dependent on the value constructor of a Property
value.
This isn't using xpTextAttr
correctly. First of all its first parameter should be the attribute name "property"
and secondly, it returns the text it matches.
You want to return the constructor A
or B
respectively.
You need to use xpWrap
to specify the mapping (both ways) between the text contents of the property ("a"
or "b"
) and those constructors. And the tags are 0-based I believe, so 0 and 1.
where
tag A = 0
tag B = 1
ps = [ xpWrap (const A,const "a") $ xpTextAttr "property"
, xpWrap (const B,const "b") $ xpTextAttr "property"
]
Then the call to xpAttr
is wrong. Honestly I'm not sure what xpAttr
is for, something to do with qualified names. In fact sufficient code for xpProperty
is
xpProperty :: PU (Maybe Property)
xpProperty = xpOption $ xpPropertyValue