@@ -3,6 +3,7 @@ package org.kson.ast
33import org.kson.CompileTarget
44import org.kson.CompileTarget.Kson
55import org.kson.CompileTarget.Yaml
6+ import org.kson.CompileTarget.Json
67import org.kson.ast.AstNode.Indent
78import org.kson.parser.EMBED_DELIMITER
89import org.kson.parser.NumberParser
@@ -109,7 +110,7 @@ class KsonRoot(
109110 */
110111 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
111112 return when (compileTarget) {
112- is Kson , is Yaml -> {
113+ is Kson , is Yaml , is Json -> {
113114 rootNode.toSource(indent, compileTarget) +
114115 if (compileTarget.preserveComments && documentEndComments.isNotEmpty()) {
115116 " \n\n " + documentEndComments.joinToString(" \n " )
@@ -127,7 +128,7 @@ class ObjectDefinitionNode(private val internalsNode: ObjectInternalsNode) :
127128 ValueNode () {
128129 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
129130 return when (compileTarget) {
130- is Kson , is Yaml -> {
131+ is Kson , is Yaml , is Json -> {
131132 internalsNode.toSource(indent, compileTarget)
132133 }
133134 }
@@ -148,12 +149,24 @@ class ObjectInternalsNode(private val properties: List<ObjectPropertyNode>) : Va
148149 """ .trimMargin()
149150 }
150151 }
152+
153+ is Json -> {
154+ if (properties.isEmpty()) {
155+ " ${indent.firstLineIndent()} {}"
156+ } else {
157+ """
158+ |${indent.firstLineIndent()} {
159+ |${properties.joinToString(" ,\n " ) { it.toSource(indent.next(false ), compileTarget) }}
160+ |${indent.bodyLinesIndent()} }
161+ """ .trimMargin()
162+ }
163+ }
151164
152165 is Yaml -> {
153166 if (properties.isEmpty()) {
154167 indent.firstLineIndent() + " {}"
155168 } else {
156- properties.joinToString(" \n " ) {
169+ properties.joinToString(" \n " ) {
157170 it.toSource(indent, compileTarget)
158171 }
159172 }
@@ -170,7 +183,7 @@ class ObjectPropertyNode(
170183 AstNode (), Documented {
171184 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
172185 return when (compileTarget) {
173- is Kson -> {
186+ is Kson , is Json -> {
174187 " ${name.toSource(indent, compileTarget)} : ${
175188 value.toSource(
176189 indent.clone(true ),
@@ -194,7 +207,7 @@ class ObjectPropertyNode(
194207class ListNode (private val elements : List <ListElementNode >) : ValueNode() {
195208 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
196209 return when (compileTarget) {
197- is Kson -> {
210+ is Kson , is Json -> {
198211 // We pad our list bracket with newlines if our list is non-empty
199212 val bracketPadding = if (elements.isEmpty()) " " else " \n "
200213 val endBraceIndent = if (elements.isEmpty()) " " else indent.bodyLinesIndent()
@@ -221,7 +234,7 @@ class ListNode(private val elements: List<ListElementNode>) : ValueNode() {
221234class ListElementNode (val value : ValueNode , override val comments : List <String >) : AstNode(), Documented {
222235 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
223236 return when (compileTarget) {
224- is Kson -> {
237+ is Kson , is Json -> {
225238 value.toSource(indent, compileTarget)
226239 }
227240 is Yaml -> {
@@ -245,6 +258,10 @@ open class StringNode(override val stringContent: String) : KeywordNode() {
245258 is Kson , is Yaml -> {
246259 indent.firstLineIndent() + " \" " + stringContent + " \" "
247260 }
261+
262+ is Json -> {
263+ indent.firstLineIndent() + " \" ${escapeJsonString(stringContent)} \" "
264+ }
248265 }
249266 }
250267}
@@ -255,6 +272,10 @@ class IdentifierNode(override val stringContent: String) : KeywordNode() {
255272 is Kson , is Yaml -> {
256273 indent.firstLineIndent() + stringContent
257274 }
275+
276+ is Json -> {
277+ indent.firstLineIndent() + " \" ${escapeJsonString(stringContent)} \" "
278+ }
258279 }
259280 }
260281}
@@ -271,7 +292,7 @@ class NumberNode(stringValue: String) : ValueNode() {
271292
272293 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
273294 return when (compileTarget) {
274- is Kson , is Yaml -> {
295+ is Kson , is Yaml , is Json -> {
275296 indent.firstLineIndent() + value.asString
276297 }
277298 }
@@ -281,7 +302,7 @@ class NumberNode(stringValue: String) : ValueNode() {
281302class TrueNode : ValueNode () {
282303 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
283304 return when (compileTarget) {
284- is Kson , is Yaml -> {
305+ is Kson , is Yaml , is Json -> {
285306 indent.firstLineIndent() + " true"
286307 }
287308 }
@@ -291,7 +312,7 @@ class TrueNode : ValueNode() {
291312class FalseNode : ValueNode () {
292313 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
293314 return when (compileTarget) {
294- is Kson , is Yaml -> {
315+ is Kson , is Yaml , is Json -> {
295316 indent.firstLineIndent() + " false"
296317 }
297318 }
@@ -301,7 +322,7 @@ class FalseNode : ValueNode() {
301322class NullNode : ValueNode () {
302323 override fun toSourceInternal (indent : Indent , compileTarget : CompileTarget ): String {
303324 return when (compileTarget) {
304- is Kson , is Yaml -> {
325+ is Kson , is Yaml , is Json -> {
305326 indent.firstLineIndent() + " null"
306327 }
307328 }
@@ -342,6 +363,20 @@ class EmbedBlockNode(private val embedTag: String, private val embedContent: Str
342363 renderMultilineYamlString(embedContent, indent, indent.next(false ))
343364 }
344365 }
366+
367+ is Json -> {
368+ if (! compileTarget.retainEmbedTags) {
369+ indent.firstLineIndent() + " \" ${escapeJsonString(embedContent)} \" "
370+ } else {
371+ val nextIndent = indent.next(false )
372+ """
373+ |${indent.firstLineIndent()} {
374+ |${nextIndent.bodyLinesIndent()} "$EMBED_TAG_KEYWORD ": "$embedTag ",
375+ |${nextIndent.bodyLinesIndent()} "$EMBED_CONTENT_KEYWORD ": "${escapeJsonString(embedContent)} "
376+ |}
377+ """ .trimMargin()
378+ }
379+ }
345380 }
346381 }
347382}
@@ -375,3 +410,20 @@ private fun renderMultilineYamlString(
375410 contentIndent.bodyLinesIndent() + line
376411 }
377412}
413+
414+ /* *
415+ * Escapes a string for use in JSON
416+ *
417+ * @param str The string to escape
418+ * @return The escaped string with all JSON special characters properly escaped
419+ */
420+ private fun escapeJsonString (str : String ): String {
421+ // This function escapes JSON characters
422+ return str
423+ .replace(" \\ " , " \\\\ " ) // Must be first to avoid double-escaping
424+ .replace(" \" " , " \\\" " )
425+ .replace(" \b " , " \\ b" )
426+ .replace(" \n " , " \\ n" )
427+ .replace(" \r " , " \\ r" )
428+ .replace(" \t " , " \\ t" )
429+ }
0 commit comments