Skip to content

Commit

Permalink
Added a .toJson method to each protobuf message.
Browse files Browse the repository at this point in the history
Enable this functionality with --generate_json_method. Without this option enabled, a dummy method will be generated.
  • Loading branch information
SandroGrzicic committed Aug 12, 2013
1 parent 155a55c commit 147509e
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 15 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Both the ScalaBuff generator and the generated Scala classes depend on Google's
If you want to utilize ScalaBuff to generate your Scala classes from .proto sources, you'll need to either [download the source](https://github.com/SandroGrzicic/ScalaBuff/archive/master.zip) or download the packaged JAR for your Scala version from the Sonatype OSS repository. If you download the sources, you can easily run it from SBT.

If you just want to use ScalaBuff-generated classes in your SBT-managed project, here's the dependency to add (located on the Sonatype OSS repository): `"net.sandrogrzicic" %% "scalabuff-runtime" % "[desired_version]"`
The latest release is **1.3.5** with support for Scala 2.9.3, 2.10 and 2.11.0-M3.
The latest release is **1.3.6** with support for Scala 2.9.3, 2.10 and 2.11.0-M4.

If you'd like to use SBT with ScalaBuff to auto-generate Scala protobuf classes from .proto sources, try the [sbt-scalabuff project](https://github.com/sbt/sbt-scalabuff).

Expand Down
6 changes: 3 additions & 3 deletions project/ScalaBuffBuild.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ object ScalaBuffBuild extends Build {
lazy val buildSettings = Seq(
name := "ScalaBuff",
organization := "net.sandrogrzicic",
version := "1.3.5",
version := "1.3.6",
scalaVersion := "2.10.2",
//scalaVersion := "2.11.0-M3",
//scalaBinaryVersion := "2.11.0-M3",
//scalaVersion := "2.11.0-M4",
//scalaBinaryVersion := "2.11.0-M4",
logLevel := Level.Info
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,24 @@ class BuffedString(str: String) {
if (fromPos < 0) fromPos = 0
if (toPos < 0) toPos = str.length
if (fromPos > toPos) ""
str.substring(fromPos, toPos)
else str.substring(fromPos, toPos)
}


/**
* Returns the substring between the specified characters.
* If any of the characters isn't found, the returned string is returned fully from the start and/or
* to the end of the original string.
* If the end position is lower than the start position, an empty string is returned.
*/
def between(from: Char, to: Char) = {
var fromPos = str.indexOf(from) + 1
var toPos = str.lastIndexOf(to, from)
if (fromPos < 0) fromPos = 0
if (toPos < 0) toPos = str.length
if (fromPos > toPos) ""
else str.substring(fromPos, toPos)
}

/**
* Removes leading and trailing double quotes from this string, if any.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import java.io.File
* @author Sandro Gržičić
*/

class Generator protected (sourceName: String, importedSymbols: Map[String, ImportedSymbol]) {
class Generator protected (sourceName: String, importedSymbols: Map[String, ImportedSymbol], generateJsonMethod: Boolean) {
import Generator._

protected val imports = mutable.ListBuffer[String]()
Expand Down Expand Up @@ -343,6 +343,51 @@ class Generator protected (sourceName: String, importedSymbols: Map[String, Impo
.append(indent1).append("def newBuilderForType = throw new RuntimeException(\"Method not available.\")\n")
.append(indent1).append("def toBuilder = throw new RuntimeException(\"Method not available.\")\n")
}

// toJson
if (generateJsonMethod) {
out
.append(indent1).append("def toJson(indent: Int = 0): String = {\n")
.append(indent2).append("val indent0 = \"\\n\" + (\"\\t\" * indent)\n")
.append(indent2).append("val (indent1, indent2) = (indent0 + \"\\t\", indent0 + \"\\t\\t\")\n")
.append(indent2).append("val sb = StringBuilder.newBuilder\n")
.append(indent2).append("sb\n")
.append(indent3).append(".append(\"{\")\n")

for (field <- fields) {
val name = field.name.lowerCamelCase
val (quotesStart, quotesEnd) = if (!field.fType.isMessage) (".append(\"\\\"\")", ".append(\"\\\"\")") else ("", "")
val mapQuotes = if (!field.fType.isMessage) ".map(\"\\\"\" + _ + \"\\\"\")" else ""
val toJson = if (field.fType.isMessage) ".toJson(indent + 1)" else ""
val mapToJson = if (field.fType.isMessage) ".map(_.toJson(indent + 1))" else ""

field.label match {
case REQUIRED =>
out.append(indent3).append("sb.append(indent1).append(\"\\\"").append(name)
.append("\\\": \")").append(quotesStart).append(".append(`").append(name)
.append("`").append(toJson).append(")").append(quotesEnd).append(".append(',')").append('\n')
case OPTIONAL =>
out.append(indent3)
.append(s"if (`$name`.isDefined) { ").append("sb.append(indent1).append(\"\\\"").append(name)
.append("\\\": \")").append(quotesStart).append(".append(`").append(name)
.append(s"`.get$toJson)").append(quotesEnd).append(".append(',') }").append('\n')
case REPEATED =>
out.append(indent3).append("sb.append(indent1).append(\"\\\"").append(name).append("\\\": [\")")
.append(".append(indent2).append(`").append(name).append("`").append(mapToJson).append(mapQuotes)
.append(".mkString(\", \" + indent2)).append(indent1).append(']').append(',')").append('\n')
case _ =>
}
}
out.append(indent2).append("sb.length -= 1\n")
out.append(indent2).append("sb.append(indent0).append(\"}\")\n")

out.append(indent2).append("sb.toString()\n")
out.append(indent1).append("}\n\n")

} else {
out.append(indent1).append("def toJson(indent: Int = 0): String = \"ScalaBuff JSON generation not enabled. Use --generate_json_method to enable.\"\n")
}

out.append(indent0).append("}\n\n")

// *** companion object
Expand Down Expand Up @@ -487,8 +532,8 @@ object Generator {
/**
* Returns a valid Scala class.
*/
def apply(tree: List[Node], sourceName: String, importedSymbols: Map[String, ImportedSymbol]): ScalaClass = {
new Generator(sourceName, importedSymbols).generate(tree)
def apply(tree: List[Node], sourceName: String, importedSymbols: Map[String, ImportedSymbol], generateJsonMethod: Boolean): ScalaClass = {
new Generator(sourceName, importedSymbols, generateJsonMethod).generate(tree)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ object ScalaBuff {
inputEncoding: Charset = defaultCharset,
outputEncoding: Charset = defaultCharset,
verbose: Boolean = false,
extraVerbose: Boolean = false)
extraVerbose: Boolean = false,
generateJsonMethod: Boolean = false)

val defaultSettings = Settings()

Expand All @@ -30,13 +31,15 @@ object ScalaBuff {
def apply(file: File)(implicit settings: Settings = defaultSettings) = {
val tree = parse(file)
val symbols = processImportSymbols(tree)
Generator(tree, file.getName, symbols)
Generator(tree, file.getName, symbols, settings.generateJsonMethod)
}

/**
* Runs ScalaBuff on the specified input String and returns the output Scala class.
*/
def fromString(input: String) = Generator(Parser(input), "", Map())
def fromString(input: String, generateJsonMethod: Boolean = false) = {
Generator(Parser(input), "", Map(), generateJsonMethod)
}

/**
* Parse a protobuf file into Nodes.
Expand Down Expand Up @@ -229,6 +232,9 @@ object ScalaBuff {
case ue: UnsupportedEncodingException => Strings.UNSUPPORTED_OUTPUT_ENCODING + outputEncoding
}

case "--generate_json_method" =>
settings.copy(generateJsonMethod = true)

case unknown =>
Strings.UNKNOWN_ARGUMENT + unknown

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ Parse protocol buffer files and generate output based on the options given:
(if not specified, current directory is used).
--stdout Output all results to stdout, do not write any
files.
--generate_json_method Generate a toJson method for each case class.
--proto_encoding=ENC Use ENC as the encoding of the input files.
--out_encoding=ENC Use ENC as the encoding of the output files.
-h, --help Show this help text and exit.
"""
"""

val UNKNOWN_INPUT = "<unknown>"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ object UpdateTestResources extends App {
// if we have a valid parsing tree, generate a Scala proto class.

// for now, this is hard-coded.
val importedSymbols = Map("PackageTest" -> ImportedSymbol("nested", false))
val importedSymbols = Map("PackageTest" -> ImportedSymbol("nested", isEnum = false))

val generated = Generator(parsed, file.getName, importedSymbols)
val generated = Generator(parsed, file.getName, importedSymbols, generateJsonMethod = false)
val generatedPath = testDir + generated.path + generated.file + ".scala"

new File(testDir + generated.path).mkdirs()
Expand Down

0 comments on commit 147509e

Please sign in to comment.