使用HXT基于父元素子节点内容插入新元素的技术问题
解决HXT中插入动态内容节点的类型错误问题
错误原因
你写的snm和gnm是箭头类型(a XmlTree String),代表从XmlTree中提取字符串的数据流操作,但L.concat需要的是纯字符串列表。直接把箭头当作字符串用,必然触发类型不匹配——HXT的箭头是用于处理XML数据流的管道,不是直接返回值的函数。
核心思路
要在<Slip>节点内动态生成新元素,必须先从当前<Slip>节点中提取所需的snm和gnm文本,再基于这些文本构建新节点,最后将新节点插入到原节点的子节点列表头部。关键是用HXT的箭头组合子(比如(&&&))并行提取多个值,再用arr将纯函数应用到提取出的值上。
修正后的完整代码
import Text.XML.HXT.Core import qualified Data.Text.Lazy as L testHXT :: IO () testHXT = do runX $ readDocument [withValidate no] "input.xml" >>> processChildren (processSlips `when` isElem) >>> writeDocument [withIndent yes] "output.xml" return () processSlips :: ArrowXml a => a XmlTree XmlTree processSlips = processTopDown (addAccountNo `when` isSlip) where isSlip = isElem >>> hasName "Slip" -- 核心修改:先提取当前Slip的snm和gnm,再构建新节点+原节点子节点 addAccountNo = -- 并行提取snm和gnm文本,得到箭头:a XmlTree (String, String) (getChildren >>> hasName "BNFY_NM" >>> getChildren >>> hasName "snm" >>> getText) &&& (getChildren >>> hasName "BNFY_NM" >>> getChildren >>> hasName "gnm" >>> getText) -- 将提取到的两个字符串拼接成账号文本 >>> arr (\(snmTxt, gnmTxt) -> L.concat [snmTxt, " ", gnmTxt]) -- 从拼接后的文本生成SlipSupplementary节点 >>> arr (\acctTxt -> mkelem "SlipSupplementary" [] [mkelem "dlr_acct_nbr" [] [txt acctTxt]]) -- 将新节点与原Slip的子节点拼接,替换原Slip的子节点 >>> (\newNode -> replaceChildren (newNode <+> getChildren)) -- 可选:封装复用提取逻辑 -- getField fieldName = getChildren >>> hasName "BNFY_NM" >>> getChildren >>> hasName fieldName >>> getText
关键修改说明
- 精准定位节点:用
getChildren >>> hasName "BNFY_NM"替代deep,避免匹配到<Slip>外的同名节点,提升可靠性。 - 并行提取数据:
(&&&)箭头组合子可以同时从同一个XmlTree中提取snm和gnm的文本,得到一个包含两个字符串的元组。 - 纯函数处理:
arr用于将纯字符串拼接函数(\(s,g) -> ...)转换成箭头,适配HXT的数据流模型。 - 动态构建节点:基于拼接后的文本生成
<SlipSupplementary>节点,再将其插入到原<Slip>的子节点列表最前面。
扩展性说明
如果需要添加更多字段(比如从其他子节点提取内容),只需扩展(&&&)的组合:
-- 示例:同时提取snm、gnm、另一个字段 (getField "snm") &&& (getField "gnm") &&& (getField "other_field") >>> arr (\((s,g),o) -> L.concat [s, " ", g, "_", o])
内容的提问来源于stack exchange,提问作者Neil
相关产品推荐
相关产品推荐

