https://github.com/iadvize/xyleme
A quick way to parse XML structures
https://github.com/iadvize/xyleme
cats parser scala xml
Last synced: 12 months ago
JSON representation
A quick way to parse XML structures
- Host: GitHub
- URL: https://github.com/iadvize/xyleme
- Owner: iadvize
- License: mit
- Created: 2022-04-06T07:05:52.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2023-04-07T21:01:30.000Z (about 3 years ago)
- Last Synced: 2025-01-29T07:14:47.320Z (over 1 year ago)
- Topics: cats, parser, scala, xml
- Language: Scala
- Homepage:
- Size: 34.2 KB
- Stars: 0
- Watchers: 6
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Xyleme
_A quick way to parse XML structures_
[](https://codecov.io/gh/iadvize/xyleme)
## Motivation
Xyleme comes from the frustration of having to quickly parse XML elements.
In my experience, two well-known libraries enable to parse XML in Scala :
- native [scala-xml](https://github.com/scala/scala-xml) enables fast formatting, but is tedious while parsing
- [scalaxb](https://github.com/eed3si9n/scalaxb) is safer, but needs XSD to be defined and sometimes is not ideal (complex structure, duplicate names)
The idea of Xyleme is to use [circe](https://github.com/circe/circe) as inspiration to enable safer and easier parsing of xml elements.
## Getting started
First, add Xyleme dependency to your module definition in `build.sbt`
```sbt
libraryDependencies += "com.iadvize" %% "xyleme" % ""
```
It brings to transitive dependencies : [cats-core](https://github.com/typelevel/cats) and [scala-xml](https://github.com/scala/scala-xml)
Then, use `ElemDecoder` and `ElemCursor` to parse your nodes
```scala
import com.iadvize.xyleme._
import scala.util.Try
import cats.syntax.apply._
case class Price(value: Long)
case class Album(name: String, artist: String, price: Price)
implicit val priceDecoder: TextDecoder[Price] = TextDecoder.fromOption("Price") { str =>
Try((BigDecimal(str) * 100).toLongExact).toOption.map(Price)
}
implicit val decoder: ElemDecoder[Album] = ElemDecoder.instance { elem =>
(
elem.downElem("name").text.as[String],
elem.downElem("artist").text.as[String],
elem.attribute("price").as[Price]
).mapN(Album)
}
val xml =
One Size Fits All
Frank Zappa
Maggot Brain
Funkadelic
val albums = ElemCursor.from(xml).downElem("album").as[List[Album]]
println(albums)
// Valid(List(
// Album(One Size Fits All,Frank Zappa,Price(1500)),
// Album(Maggot Brain,Funkadelic,Price(1300))
// ))
```
Xyleme is able to detect formatting errors. These are accumulated thanks to the [ValidatedNec](https://typelevel.org/cats/datatypes/validated.html) datatype
```scala
val wrongXml =
One Size Fits All
Frank Zappa
Maggot Brain
val wrongAlbums = ElemCursor.from(wrongXml).downElem("album").as[List[Album]]
println(wrongAlbums)
// Invalid(Chain(
// Failed to parse field into Price, path: /albums/album/@price, text: not-a-price,
// Failed to find field at given path: /albums/album[2]/artist/text()
// ))
```
## Contribute
Xyleme is currently in development :
- its API is not stable and could change
- it certainly has a lot of edge cases
- its current implementation is not optimized (multiple traversals of DOM)
Feel free to contribute :). I already think of several improvements:
- a stax cursor implementation, quite complex, but I think it could be possible
- add property based tests to improve coverage of edge cases
Contribution guidelines are described in [CONTRIBUTING.md](CONTRIBUTING.md).