XML decoder module sharing the spirit of Json.Decode.
Using XmlParser as its parser component.
Examples in this package are doc-tested.
exampleDecoder : Decoder ( String, List Int )
exampleDecoder =
map2 Tuple.pair
(path [ "string", "value" ] (single string))
(path [ "int", "values" ] (list int))
run exampleDecoder
"""
<root>
<string>
<value>SomeString</value>
</string>
<int>
<values>1</values>
<values>2</values>
</int>
</root>
"""
--> Ok ( "SomeString", [ 1, 2 ] )A function that knows how to decode list of XML nodes into Elm value.
Used in conjunction with "query" functions such as path.
Represents error on decode execution.
DetailedError will tell you:
Possible problems that can happen during decoding.
Synonym of decodeString.
Parses an XML string and decodes into Elm value.
Parsing will be done by XmlParser.parse
This function returns error in String.
Should you need to separately handle Parser.Error and Xml.Decode.Error,
explicitly use XmlParser.parse and decodeXml.
Decodes an XmlParser.Xml value into other type of Elm value.
It discards Document Type Definitoin (DTD) and Processing Instruction in XML, only cares about root XML node.
import XmlParser exposing (Xml, Node(..))
exampleDecoder : Decoder ( String, List Int )
exampleDecoder =
map2 Tuple.pair
(path [ "string", "value" ] (single string))
(path [ "int", "values" ] (list int))
decodeXml exampleDecoder <|
Xml [] Nothing <|
Element "root" []
[ Element "string" []
[ Element "value" [] [ Text "SomeString" ]
]
, Element "int" []
[ Element "values" [] [ Text "1" ]
, Element "values" [] [ Text "2" ]
]
]
--> Ok ( "SomeString", [ 1, 2 ] )Decodes an XmlParser.Node into String.
XmlParser.Text, extracts its value.XmlParser.Element AND contains nothing, treat it as "empty text".XmlParser.Element AND contains a single XmlParser.Text child,
extracts its value.If you want to extract values from node attribute, use stringAttr and variants.
run string "string" --> Ok "string"
run string ""
--> Ok ""
run string "string"
--> Err "The node is not a simple text node. At: /, Node: string"Similar to string, but also tries to convert String to Int.
run int "<root>1</root>"
--> Ok 1
run int "<root>value</root>"
--> Err "could not convert string 'value' to an Int At: /, Node: <root>value</root>"Decodes to Float.
run float "<root>1.0</root>"
--> Ok 1.0
run float "<root>value</root>"
--> Err "could not convert string 'value' to a Float At: /, Node: <root>value</root>"Decodes to Bool.
In Xml Schema Definition (XSD), valid lexical representation of boolean values are,
We follow this specification, case-sensitively.
run bool "true" --> Ok True
run bool "value"
--> Err "Not a valid boolean value. At: /, Node: value"Decodes an attribute value of XmlParser.Node into String.
Fails if the node does not have specified attribute.
run (stringAttr "attr") "<root attr='value'></root>"
--> Ok "value"
run (stringAttr "attr") "<root></root>"
--> Err "Attribute 'attr' not found. At: /, Node: <root></root>"Decodes an attribute value into Int.
run (intAttr "attr") "<root attr='1'></root>"
--> Ok 1
run (intAttr "attr") "<root attr='value'></root>"
--> Err "could not convert string 'value' to an Int At: /, Node: <root attr=\"value\"></root>"Decodes an attribute value into Float.
run (floatAttr "attr") "<root attr='1.5'></root>"
--> Ok 1.5
run (floatAttr "attr") "<root attr='value'></root>"
--> Err "could not convert string 'value' to a Float At: /, Node: <root attr=\"value\"></root>"Decodes an attribute value into Bool.
run (boolAttr "attr") "<root attr='true'></root>"
--> Ok True
run (boolAttr "attr") "<root attr='value'></root>"
--> Err "Not a valid boolean value. At: /, Node: <root attr=\"value\"></root>"Composes ListDecoder that results in a singular value.
It fails if:
Examples:
run (path [ "tag" ] (single string)) "<root><tag>string</tag></root>"
--> Ok "string"
run (path [ "tag" ] (single string)) "<root></root>"
--> Err "Node not found. At: /tag, Node: <root></root>"
run (path [ "tag" ] (single string)) "<root><tag>string1</tag><tag>string2</tag></root>"
--> Err "Multiple nodes found. At: /tag, Node: <root><tag>string1</tag><tag>string2</tag></root>"Composes ListDecoder that results in a list of values.
This ListDecoder fails if any incoming items cannot be decoded.
run (path [ "tag" ] (list string)) "<root><tag>string1</tag><tag>string2</tag></root>"
--> Ok [ "string1", "string2" ]
run (path [ "tag" ] (list int)) "<root><tag>1</tag><tag>nonInt</tag></root>"
--> Err "could not convert string 'nonInt' to an Int At: /tag, Node: <tag>nonInt</tag>"Variation of list, which ignores items that cannot be decoded.
run (path [ "tag" ] (leakyList int)) "<root><tag>1</tag><tag>nonINt</tag></root>"
--> Ok [ 1 ]mapN series are backed by Result.mapN series, thus it only supports up to map5.
Generates a decoder that depends on previous value.
Transform a decoder.
run (map String.length string) "<root>string</root>"
--> Ok 6Generates a decoder that combines results from two decoders.
It can be used for generating a decoder for a data type that takes two inputs. Also this is used as a building block of decoder composition helpers.
run (map2 Tuple.pair string string) "<root>string</root>"
--> Ok ( "string", "string" )Equivalent to Json.Decode.Extra.andMap, allows writing XML decoders in sequential style.
run (succeed Tuple.pair |> andMap (path ["string"] (single string)) |> andMap (path ["int"] (single int)) ) "string1" --> Ok ("string", 1)
Generates a decoder that results in the default value on failure.
run (withDefault 0 int) "<root>1</root>"
--> Ok 1
run (withDefault 0 int) "<root>Non Int value</root>"
--> Ok 0Generates a decoder that results in a Maybe value.
If the given decoder resulted in Err, it succeeds with Nothing.
Otherwise (in cases of Ok,) it succeeds with Just value.
run (maybe int) "<root>1</root>"
--> Ok (Just 1)
run (maybe int) "<root>Non Int value</root>"
--> Ok NothingGenerates a lazy decoder.
Similar to Json.Decode.lazy.
someRecordDecoder : Decoder SomeRecord
someRecordDecoder =
map2 SomeRecord
(path [ "path", "to", "string", "value" ] (single string))
(path [ "path", "to", "list", "of", "someRecord" ] (list (lazy (\_ -> someRecordDecoder))))With this, you can avoid "bad-recursion" error on compilation
which happens when you define nested part of the above decoder as (list someRecordDecoder)
Generates a Decoder that applies a ListDecoder at specified XML path.
Typical usage:
someRecordDecoder : Decoder SomeRecord
someRecordDecoder =
map2 SomeRecord
(path [ "string", "value" ] (single string))
(path [ "int", "values" ] (list int))Due to the nature of XML, you cannot distinguish a particular tag or tags hold whether "singular value" or "list of values", from the structure of XML document itself. This is opposite to JSON, where there can be only one field of a paticular key in a level, and its quantization is obvious from the shape of the value.
For this reason, this function always produce list of XmlParser.Nodes.
Then they must be decoded using special decoder parts: ListDecoder.
In the above example, single and list are ListDecoder generators.
If you want to perform complex resolution of multiple matches from path,
you can implement your own ListDecoders.
Note that in the path, you must "start" at the root scope. For instance, to work with an XML document like:
<Root> <Path> <Target> Value </Target> </Path> </Root>
You should specify:
path [ "Path", "Target" ] (single string)Basic usages:
run (path [ "tag", "nested" ] (single string)) "<root><tag><nested>string1</nested></tag></root>"
--> Ok "string1"
run (path [ "tag", "nested" ] (list string)) "<root><tag><nested>string1</nested><nested>string2</nested></tag></root>"
--> Ok [ "string1", "string2" ]
run (path [ "tag", "nested" ] (single (stringAttr "value"))) "<root><tag><nested value='attr1'></nested></tag></root>"
--> Ok "attr1"Decoders will report errors with path at which error happened as well as nearest node:
run (path [ "tag", "nested" ] (single int)) "<root><tag><nested>string1</nested></tag></root>"
--> Err "could not convert string 'string1' to an Int At: /tag/nested, Node: <nested>string1</nested>"Allow writing Decoders in Pipeline-style, just like Json.Decode.Pipeline.
Decodes value at required XML path.
pipelineDecoder : Decoder ( String, List Int )
pipelineDecoder =
succeed Tuple.pair
|> requiredPath [ "path", "to", "string", "value" ] (single string)
|> requiredPath [ "path", "to", "int", "values" ] (list int)
run pipelineDecoder
"""
<root>
<path>
<to>
<string>
<value>SomeString</value>
</string>
<int>
<values>1</values>
<values>2</values>
</int>
</to>
</path>
</root>
"""
--> Ok ( "SomeString", [ 1, 2 ] )Tries to decode value at optional XML path. Uses default value if the node is missing.
decoderWithDefault : Decoder String
decoderWithDefault =
succeed identity
|> optionalPath [ "optional", "path" ] (single string) "default"
run decoderWithDefault "<root><optional><path>string</path></optional></root>"
--> Ok "string"
run decoderWithDefault "<root></root>"
--> Ok "default"Decodes value at possible XML path into Maybe value.
maybeDecoder : Decoder (Maybe String)
maybeDecoder =
succeed identity
|> possiblePath [ "possible", "path" ] (single string)
run maybeDecoder "<root><possible><path>string</path></possible></root>"
--> Ok (Just "string")
run maybeDecoder "<root></root>"
--> Ok NothingIf you want to apply default value when the node is missing, use optionalWith.
Convert Error to a formatted string.
A function that knows how to decode an XML node into Elm value.