나는 하스켈에 간단한 XML 파서를 작성했다. convertXML 함수는 XML 파일의 내용을 받아서 처리 된 추출 값의 목록을 반환합니다.IO 코드로 순수 함수를 확장 할 수 있습니까?
XML 태그의 속성 중 하나에도 제품 이미지의 URL이 포함되어 있으며 태그가 발견되면 다운로드하는 기능을 확장하고 싶습니다.
convertXML :: (Text.XML.Light.Lexer.XmlSource s) => s -> [String]
convertXML xml = productToCSV products
where
productToCSV [] = []
productToCSV (x:xs) = (getFields x) ++ (productToCSV
(elChildren x)) ++ (productToCSV xs)
getFields elm = case (qName . elName) elm of
"product" -> [attrField "uid", attrField "code"]
"name" -> [trim $ strContent elm]
"annotation" -> [trim $ strContent elm]
"text" -> [trim $ strContent elm]
"category" -> [attrField "uid", attrField "name"]
"manufacturer" -> [attrField "uid",
attrField "name"]
"file" -> [getImgName]
_ -> []
where
attrField fldName = trim . fromJust $
findAttr (unqual fldName) elm
getImgName = if (map toUpper $ attrField "type") == "FULL"
then
-- here I need some IO code
-- to download an image
-- fetchFile :: String -> IO String
attrField "file"
else []
products = findElements (unqual "product") productsTree
productsTree = fromJust $ findElement (unqual "products") xmlTree
xmlTree = fromJust $ parseXMLDoc xml
getImgName에 기능을 IO 코드를 삽입하거나 내가 완전히 순수 버전으로 convertXML 기능을 다시 작성해야 할 방법을 어떤 생각?
업데이트 2 최종 convertXML 기능 버전입니다. 하이브리드는 순수하고 불결하지만 깨끗한 방법으로 Carl이 제안했습니다. 반환 된 쌍의 두 번째 매개 변수는 다운로드 및 디스크에 저장하는 이미지를 실행하고 저장되는 이미지가있는 로컬 경로의 목록을 래핑하는 IO 작업입니다.
convertXML :: (Text.XML.Light.Lexer.XmlSource s) => s -> ([String], IO [String])
convertXML xml = productToCSV products (return [])
where
productToCSV :: [Element] -> IO String -> ([String], IO [String])
productToCSV [] _ = ([], return [])
productToCSV (x:xs) (ys) = storeFields (getFields x)
(storeFields (productToCSV (elChildren x) (return []))
(productToCSV xs ys))
getFields elm = case (qName . elName) elm of
"product" -> ([attrField "uid", attrField "code"], return [])
"name" -> ([trim $ strContent elm], return [])
"annotation" -> ([trim $ strContent elm], return [])
"text" -> ([trim $ strContent elm], return [])
"category" -> ([attrField "uid", attrField "name"], return [])
"manufacturer" -> ([attrField "uid",
attrField "name"], return [])
"file" -> getImg
_ -> ([], return [])
where
attrField fldName = trim . fromJust $
findAttr (unqual fldName) elm
getImg = if (map toUpper $ attrField "type") == "FULL"
then
([attrField "file"], fetchFile url >>=
saveFile localPath >>
return [localPath])
else ([], return [])
where
fName = attrField "file"
localPath = imagesDir ++ "/" ++ fName
url = attrField "folderUrl" ++ "/" ++ fName
storeFields (x1s, y1s) (x2s, y2s) = (x1s ++ x2s, liftM2 (++) y1s y2s)
products = findElements (unqual "product") productsTree
productsTree = fromJust $ findElement (unqual "products") xmlTree
xmlTree = fromJust $ parseXMLDoc xml
두 번째 인수로 IO 작업 유형의 "값"을 반환하는 것이 좋습니다. 어떤 방법으로 내 -> ([String], [String]) 버전을 -> ([String], IO [String])로 업데이트 할 수 있습니까? –
글쎄, 'String'이 이미지의 올바른 유형이라고 생각하지 않습니다. URL에 의해 리턴 된 바이트를 원할 경우 String이 아닌 표현을 위해 'ByteString'을 사용하고자 할 것입니다. 문자열은 문자 데이터 용이며 유니 코드 코드 포인트를 포함합니다. ByteString은 바이너리 형식의 이미지 일 수 있으므로 바이트 시퀀스를 효율적으로 처리하기위한 것입니다. URL을 취하는'fetch :: String -> IO ByteString'과 같은 함수가 주어지면 URL 목록을 IO action으로 변환하여'mapM fetch urls'로 가져올 수 있습니다. – Carl
Carl에게 감사드립니다. 나는 IO [String]에 접근하여 이미지의 URL 목록을 반환하고 디스크에 다운로드하면 그 "부작용"으로 호출 될 것이라고 생각했다. –