valentiay / phobos

Efficient and expressive XML data-binding library for Scala
Apache License 2.0
20 stars 5 forks source link

Encode invalid xml because of duplicate namespaces #16

Closed geny200 closed 4 months ago

geny200 commented 6 months ago

Hi, I got an interesting case, when we specify the namespace in the encoder and in the config, then it turns out that they are duplicated, so the final xml will be invalid

import phobos.Namespace
import phobos.configured.ElementCodecConfig
import phobos.decoding._
import phobos.derivation.semiauto._
import phobos.encoding._

object Namespace0 {
  type ns = Namespace0.type
  implicit val ns: Namespace[ns] = Namespace.mkInstance[ns]("http://lol", Some(""))
}

object Namespace3 {
  type ns = Namespace3.type
  implicit val ns: Namespace[ns] = Namespace.mkInstance[ns]("http://kek", Some(""))
}

case class Bar(foo: String)

object Bar {
  val config: ElementCodecConfig = ElementCodecConfig.default
    .withScopeDefaultNamespace(Namespace3)

  implicit val barXmlEncoder: XmlEncoder[Bar] =
    deriveXmlEncoderConfigured[Bar, Namespace0.ns]("Bar", Namespace0, config)
  implicit val barXmlDecoder: XmlDecoder[Bar] =
    deriveXmlDecoderConfigured[Bar, Namespace0.ns]("Bar", Namespace0, config)
}

object App {
  def main(args: Array[String]): Unit = {
    println(XmlEncoder[Bar].encode(Bar("qwerty")))
    // <?xml version='1.0' encoding='UTF-8'?><Bar xmlns="http://lol" xmlns="http://kek"><foo>qwerty</foo></Bar>
    // duplicated namespace!

    val withFooXml =
      """<?xml version='1.0' encoding='UTF-8'?><Bar xmlns="http://lol" xmlns="http://kek"><foo>qwerty</foo></Bar>"""
    println(XmlDecoder[Bar].decode(withFooXml))
    // {{{
    // phobos.decoding.DecodingError: Error while decoding XML: Duplicate namespace declaration for the default namespace
    // at [row,col {unknown-source}]: [1,81]
    //  In root element
    // }}}

    // Check law
    val x: Bar = Bar("qwerty")
    assert(
      XmlEncoder[Bar]
        .encode(x)
        .toOption
        .flatMap(XmlDecoder[Bar].decode(_).toOption)
        .contains(x),
    )
  }
}
valentiay commented 4 months ago

Hi! This seems to be similar with #18, this is also handled by #23