处理无序元素?

时间:2014-08-27 07:39:10

标签: haskell xml-parsing hxt

我刚开始使用HXT解析一些XML文档,并且想知道如何处理元素排序。

无序对

考虑以下两个具有等效数据的XML片段。

<!-- Version 1 -->
<logistics>
    <deliveryDate>2015-02-24T14:35:00Z</deliveryDate>
    <deliveryAddress>Street Name 12, 93483 City, Country</deliveryAddress>
</logistics>

<!-- Version 2 -->
<logistics>
    <deliveryAddress>Street Name 12, 93483 City, Country</deliveryAddress>
    <deliveryDate>2015-02-24T14:35:00Z</deliveryDate>
</logistics>

为了支持deliveryDatedeliverAddress的排序,我必须将XHT的xpPair函数替换为我自己的xpUnorderedPair函数:

xpUnorderedPair :: PU a -> PU b -> PU (a,b)
xpUnorderedPair pa pb = xpAlt (const 0) ps
    where ps = [ xpPair pa pb
               , xpWrap (swap,undefined) $ xpPair pb pa ],

允许我编写以下pickler函数:

xpLogisticsRequirements :: PU LogisticsRequirements
xpLogisticsRequirements = xpElem "logistics" $
                          xpWrap (uncurry LogisticsRequirements,\r -> (deliveryDate r,deliveryAddr r)) $
                          xpUnorderedPair (xpElem "deliveryDate" xpickle)
                                          (xpElem "deliveryAddress" xpText)

其中LogisticsRequirements的类型为UTCTime -> String -> LogisticsRequirement

无序三元组

现在我可以使用xpTriple执行相同操作,创建我的xpUnorderedTriple

xpUnorderedTriple :: PU a -> PU b -> PU c -> PU (a,b,c)
xpUnorderedTriple a' b' c' = xpAlt (const 0) ps
    where ps = [ xpWrap (\(a,b,c) -> (a,b,c),undefined) $ xpTriple a' b' c'
               , xpWrap (\(a,c,b) -> (a,b,c),undefined) $ xpTriple a' c' b'
               , xpWrap (\(b,c,a) -> (a,b,c),undefined) $ xpTriple b' c' a'
               , xpWrap (\(b,a,c) -> (a,b,c),undefined) $ xpTriple b' a' c'
               , xpWrap (\(c,a,b) -> (a,b,c),undefined) $ xpTriple c' a' b'
               , xpWrap (\(c,b,a) -> (a,b,c),undefined) $ xpTriple c' b' a' ]

我可以继续创建这些越来越大的函数(xpUnordered5将有120个排列),但这似乎不对。对于固定数字(即pair,triple,t4,t5等),我想我可以使用Template Haskell来创建函数,但是当我想解析不同元素的 list 时会发生什么。 / p>

无序列表

考虑XML输入,例如:

<myList>
    <name>MyList1</name>
    <elemA>...</elemA>
    <elemA>...</elemA>
    <elemB>...</elemB>
    <elemA>...</elemA>
    <elemB>...</elemB>
    <elemC>...</elemC>
    <elemB>...</elemB>
</myList>,

我将如何将它们变成

data MyList = MyList { name   :: String
                     , elemsA :: [ElemA]
                     , elemsB :: [ElemB]
                     , elemsC :: [ElemC] },

考虑到我有必要的泡菜功能,

instance XmlPicker ElemA where
    xpickle = xpElemA
instance XmlPicker ElemB where
    xpickle = xpElemB
instance XmlPicker ElemC where
    xpickle = xpElemC

我猜一个选项可能是对元素列表进行排序,然后应用顺序选择器

xpYogurt :: PU MyList
xpYogurt = xpElem "myList" $
         xpWrap (uncurry4 MyList,\l -> (name   l
                                       ,elemsA l
                                       ,elemsB l
                                       ,elemsC l)) $
            xp4Tuple (xpElem "name" xpPrim)
                     (xpList xpElemA)
                     (xpList xpElemB)
                     (xpList xpElemC)

但这似乎不太优雅,需要额外的排序逻辑!

第一种方法:

根据viorior的建议,可以定义数据类型:

data Elem = ElemA ElemA
          | ElemB ElemB
          | ElemC ElemC

然后将unpickled元素转换为它们各自的类型,但是这种方法的问题是,然后可以解析上面的列表,它不允许取消下面稍微修改的XML(注意{{1的新位置)元素):

<name>

1 个答案:

答案 0 :(得分:1)

到目前为止,还没有提供对实际问题的合适答案,所以这里是一种替代(并且显而易见)的XML表示方法,无论如何看起来更优雅:

<myList>
    <name>MyList1</name>
    <elements>
        <elemA>...</elemA>
        <elemA>...</elemA>
        <elemB>...</elemB>
        <elemA>...</elemA>
        <elemB>...</elemB>
        <elemC>...</elemC>
        <elemB>...</elemB>
    </elements>
</myList>.