[Haskell] XML Serialization and type constraints
Tomasz Zielonka
t.zielonka at students.mimuw.edu.pl
Wed Aug 25 03:39:38 EDT 2004
On Tue, Aug 24, 2004 at 07:35:46PM +0100, Simon D. Foster wrote:
> I'm trying to implement an extensible XML De/Serializer in Haskell for
> use with SOAP and XML Schema (using the Haskell XML Toolbox). The idea
> is you have a type-class, which is instantiated for each type you want
> to encode/de-encode. This class (atm) takes the form;
>
> class XMLSerializer a where
> encodeElements :: NamespaceTable -> Flags -> a -> [XmlFilter]
> encodeAttributes :: NamespaceTable -> Flags -> a -> [XmlFilter]
> encodeTree :: NamespaceTable -> String -> Flags -> a -> XmlFilter
> encodeTrees :: NamespaceTable -> String -> Flags -> a -> [XmlFilter]
>
> decodeAttribute :: String -> XmlTree -> Maybe a
> decodeElement :: XmlTree -> Maybe a
> decodeTree :: XmlTree -> Maybe a
> decodeTrees :: XmlTrees -> Maybe a
>
> (and a few default instances)
>
> This type-class can then be used recursively to build XML
> representations of Haskell data.
>
> I now want to expand this system to make is more extensible. For
> starters, to make it useful with SOAP, I need to add optional explicit
> typing of data. To this end I have another class; XSDType, which stores
> the XSD equivalent name and name-space for a particular Haskell type.
> This is what is used to add explicit type data to the XML documents.
> Adding this data involves adding an extra attribute to each node in the
> tree. More generally however each "Hook", which adds extra data at each
> node has type NamespaceTable -> Flags -> a -> ([XmlFilter],
> [XmlFilter]), where a is the type of the value.
>
> However, this is where the problem comes. How do I go about expressing
> that a has a constraint XSDType a? I don't want to add this constraint
> to the Serializer class itself since an XML tree may not be typed by
> XSD. Somehow I need a way of adding extra constraints to a dynamically.
Here is one possible solution. Below is a working implementation for a
simpler class scheme. You should be able to apply this to your problem,
at least in case of adding XSD types, if not generally.
{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -fallow-undecidable-instances #-}
module B where
import List
import Data.Typeable -- Just to implement one of example mixins
-- Mixin class - could have better name
class Mixin a t where
mixin :: t -> a -> (String -> String)
-- Serializer class
-- class Serializer has an additional parameter t which will be used
-- for passing a mixin to it. Also it is a subclass of Mixin a t, but
-- it doesn't mean adding unneccesary constraints to Serializer -
-- one of Mixin's implementations will be identity.
--
-- It is important that encodePrim's implementations don't call
-- directly to encodePrim, only to encode, which makes the
-- mixin work.
class Mixin a t => Serializer a t where
encodePrim :: t -> a -> String
encode :: Serializer a t => t -> a -> String
encode t x = mixin t x (encodePrim t x)
-- Serializer instances - I used undecidable instances here.
instance Mixin Int t => Serializer Int t where
encodePrim _ = show
instance Mixin Char t => Serializer Char t where
encodePrim _ = show
instance (Serializer a t, Mixin [a] t) => Serializer [a] t where
encodePrim t l = "[" ++ concat (intersperse ", " (map (encode t) l)) ++ "]"
-- example Mixins
data Id = Id
instance Mixin a Id where
mixin Id _ = id
data TypeOf = TypeOf
instance Typeable a => Mixin a TypeOf where
mixin TypeOf t s = "(" ++ s ++ " :: " ++ show (typeOf t) ++ ")"
instance Mixin a (String -> String) where
mixin f a = f
-- this one can be used for combining mixins
instance (Mixin a x, Mixin a y) => Mixin a (x, y) where
mixin (x, y) a = mixin x a . mixin y a
-- some unTypeable type
data T a = T a
instance (Serializer a t, Mixin (T a) t) => Serializer (T a) t where
encodePrim t (T a) = "(T " ++ encode t a ++ ")"
Example uses:
*B> putStrLn $ encode Id 'a'
'a'
*B> putStrLn $ encode TypeOf 'a'
('a' :: Char)
*B> putStrLn $ encode Id ([1..4] :: [Int])
[1, 2, 3, 4]
*B> putStrLn $ encode TypeOf ([1..4] :: [Int])
([(1 :: Int), (2 :: Int), (3 :: Int), (4 :: Int)] :: [Int])
*B> putStrLn $ encode (TypeOf, TypeOf) ([1..4] :: [Int])
(([((1 :: Int) :: Int), ((2 :: Int) :: Int), ((3 :: Int) :: Int), ((4 ::
Int) :: Int)] :: [Int]) :: [Int])
*B> putStrLn $ encode Id (T "Hello")
(T ['H', 'e', 'l', 'l', 'o'])
*B> putStrLn $ encode TypeOf (T "Hello")
<interactive>:1:
No instances for (Typeable (T [Char]), Show (IO ()))
arising from use of `encode' at <interactive>:1
In the second argument of `($)', namely `encode TypeOf (T "Hello")'
In the definition of `it':
it = putStrLn $ (encode TypeOf (T "Hello"))
<interactive>:1:
No instances for (Typeable (T [Char]), Show (IO ()))
arising from use of `print' at <interactive>:1
In a 'do' expression: print it
I hope that helps,
Best regards,
Tom
--
.signature: Too many levels of symbolic links
More information about the Haskell
mailing list