我刚刚开始使用 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>
为了同时支持deliveryDate
和deliverAddress
我不得不xpPair
用我自己的函数替换 XHT 的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 个排列),但这似乎不对。对于固定数字(即对、三重、t4、t5 等),我想我可以使用 Template Haskell 来创建函数,但是当我想解析不同元素的列表时会发生什么。
无序列表
考虑这样的 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
我猜一个选项可能是对元素列表进行排序,然后应用顺序pickler,
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 的元素转换为它们各自的类型,但这种方法的问题是,虽然可以解析上面的列表,但它不允许 unpickling 以下稍微修改的 XML(注意<name>
元素的新位置):
<myList>
<elemA>...</elemA>
<elemA>...</elemA>
<name>MyList1</name>
<elemB>...</elemB>
<elemA>...</elemA>
<elemB>...</elemB>
<elemC>...</elemC>
<elemB>...</elemB>
</myList>,