bizzabo / play-json-extensions

+22 field case class formatter and more for play-json
http://cvogt.org/play-json-extensions/api/
Other
196 stars 44 forks source link

Snake case behavior does not match play-json #82

Open dforciea opened 4 years ago

dforciea commented 4 years ago

I was trying to update from using the tototoshi json naming library to using the built-in snake case facility. However, I found out that the behavior of play-json-extensions does not match that or what is in the main play-json library. I think that it would make the most sense if this exactly matched the main play library.

I think the issue lies in how numbers are treated. play-json would snake case foo12Bar as foo12_bar play-json-extensions would snake case foo12Bar as foo_1_2_bar

I suggest just having play-json-extensions just delegate to the play library, since the snake case function is public: https://github.com/playframework/play-json/blob/2.8.1/play-json/shared/src/main/scala/play/api/libs/json/JsonConfiguration.scala#L99

JR-Utily commented 4 years ago

I have same issue.

Here is a quick test to demonstrate that


 case class AAA (
      variableWith1Element: String,
      aaa3E: String
  )

  object AAA {
    val formatPlay: Format[AAA] = {
      implicit val config: Aux[WithDefaultValues] = JsonConfiguration[WithDefaultValues](SnakeCase)
      Json.format[AAA]
    }
    val formatJsonX: Format[AAA] = {
      implicit val encoder: NameEncoder = CamelToSnakeNameEncoder()
      Jsonx.formatCaseClass[AAA]
    }
  }

  "aaa" should "des/ser"  in {
    val aaa= AAA("a","b")

    val jsonPlay = Json.parse( """{"variable_with1_element":"a", "aaa3_e":"b"}""" )
    val jsonX = Json.parse( """{"variable_with_1_element":"a", "aaa_3_e":"b"}""" )

    {
      implicit val format: Format[AAA] = AAA.formatPlay
      assert( aaa === jsonPlay.as[AAA] )
      assert( Json.toJson( aaa ) === jsonPlay )
    }

    {
      implicit val format: Format[AAA] = AAA.formatJsonX
      assert( aaa === jsonX.as[AAA] )
      assert( Json.toJson( aaa ) === jsonX )
    }
  }

I'd love to make my own NameEncoder, but sadly it is a sealed trait I can't inherit from, so following code does not compile

object PlaySnakeCaseNameEncoder extends NameEncoder {
  override def encode(str: String): String =
    play.api.libs.json.JsonNaming.SnakeCase.apply(str)
}
dforciea commented 3 years ago

@caente It looks like you're the committer who has done the most recent release - is there any particular set of instructions for making contributions to this repository? I just wanted to check in since I haven't heard anything in a couple of months.

astiob commented 1 year ago

For reference, here’s how we’ve been working around this in our projects:

import ai.x.play.json.{CamelToSnakeNameEncoder, NameEncoder}
import play.api.libs.json.JsonNaming.SnakeCase

implicit val encoder: NameEncoder = new CamelToSnakeNameEncoder {
  // Stock CamelToSnakeNameEncoder behaves differently from SnakeCase:
  // see https://github.com/xdotai/play-json-extensions/issues/82
  override def encode(str: String): String = SnakeCase(str)
}

NameEncoder may be sealed, but CamelToSnakeNameEncoder isn’t.