From a35edf7827fd6057d4a686a764aef1fe68ffe4cd Mon Sep 17 00:00:00 2001 From: Yaroslav Sagach Date: Wed, 3 Dec 2025 16:02:27 +0200 Subject: [PATCH 1/2] added ebaReader to sjs with query method --- .../izumi/sick/eba/cursor/TopCursorJs.scala | 10 ++++-- .../izumi/sick/eba/reader/EBAReaderJs.scala | 33 +++++++++++++++++++ .../scala/izumi/sick/jsapi/SickJsAPI.scala | 15 ++++++++- .../test/scala/io/izumi/sick/JsApiTest.scala | 8 +++++ .../eba/reader/IncrementalEBAReader.scala | 2 +- .../scala/io/izumi/sick/SickCursorTest.scala | 2 ++ json-sick-scala/npm-template/README.md | 4 +++ 7 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala diff --git a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/cursor/TopCursorJs.scala b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/cursor/TopCursorJs.scala index f1abbf9..7dbfac4 100644 --- a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/cursor/TopCursorJs.scala +++ b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/cursor/TopCursorJs.scala @@ -1,22 +1,28 @@ package izumi.sick.eba.cursor -import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel} +import izumi.sick.model.Ref +import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel} import scala.scalajs.js import scala.scalajs.js.JSConverters.* @JSExportTopLevel("TopCursor") -@JSExportAll class TopCursorJs(cursor: TopCursor) extends SickCursorJs(cursor.asInstanceOf[SickCursor]) { + + @JSExport def query(request: String): ObjectCursorJs = { new ObjectCursorJs(cursor.query(request)) } + @JSExport def getValues: js.Map[String, ObjectCursorJs] = { cursor.getValues.view.mapValues(new ObjectCursorJs(_)).toMap.toJSMap } + @JSExport def readKey(index: Int): ObjectCursorJs = { new ObjectCursorJs(cursor.readKey(index)) } + + protected[eba] def ref: Ref = cursor.ref } diff --git a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala new file mode 100644 index 0000000..be8439e --- /dev/null +++ b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala @@ -0,0 +1,33 @@ +package izumi.sick.eba.reader + +import izumi.sick.eba.cursor.{ObjectCursorJs, TopCursorJs} +import izumi.sick.model.RefKind + +import scala.scalajs.js +import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel} + +@JSExportTopLevel("EBAReader") +@JSExportAll +class EBAReaderJs(reader: IncrementalEBAReader, rootId: String) { + def query(request: String): js.Any = { + val cursor = new TopCursorJs(reader.getCursor(rootId)).query(request) + resolveCursorRef(cursor) + } + + private def resolveCursorRef(cursor: ObjectCursorJs): js.Any = { + cursor.ref.kind match { + case RefKind.TNul => cursor.asNul + case RefKind.TBit => cursor.asBool + case RefKind.TByte => cursor.asByte + case RefKind.TShort => cursor.asShort + case RefKind.TInt => cursor.asInt + case RefKind.TLng => cursor.asLong + case RefKind.TBigInt => cursor.asBigInt + case RefKind.TFlt => cursor.asFloat + case RefKind.TDbl => cursor.asDouble + case RefKind.TStr => cursor.asString + case _ => js.undefined + } + } + +} diff --git a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/jsapi/SickJsAPI.scala b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/jsapi/SickJsAPI.scala index 41fec33..ec4eb4a 100644 --- a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/jsapi/SickJsAPI.scala +++ b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/jsapi/SickJsAPI.scala @@ -4,7 +4,7 @@ import io.circe.Json import izumi.sick.SICK import izumi.sick.eba.SICKSettings import izumi.sick.eba.cursor.TopCursorJs -import izumi.sick.eba.reader.{EagerEBAReader, IncrementalEBAReader} +import izumi.sick.eba.reader.{EBAReaderJs, EagerEBAReader, IncrementalEBAReader} import izumi.sick.eba.writer.EBAWriter import izumi.sick.model.{SICKWriterParameters, TableWriteStrategy} import izumi.sick.sickcirce.CirceTraverser.* @@ -98,4 +98,17 @@ object SickJsAPI { val ebaReader = IncrementalEBAReader.openBytes(uint8ArrayToBytes(uint8Array), eagerOffsets = false) new TopCursorJs(ebaReader.getCursor(rootId)) } + + /** + * Alternative method for navigating the sick structure, which has query method with jq like requests + * + * `{ data: { a: 2, b: { c: [1, 2, 3] } } }` + * `const reader = ebaReaderFromUint8Array(uint8Array, "data")` + * `reader.query("b.c.[1]")` + */ + @JSExportTopLevel("ebaReaderFromUint8Array") + def ebaReaderFromUint8Array(uint8Array: Uint8Array, rootId: String): EBAReaderJs = { + val ebaReader = IncrementalEBAReader.openBytes(uint8ArrayToBytes(uint8Array), eagerOffsets = false) + new EBAReaderJs(ebaReader, rootId) + } } diff --git a/json-sick-scala/json-sick/.js/src/test/scala/io/izumi/sick/JsApiTest.scala b/json-sick-scala/json-sick/.js/src/test/scala/io/izumi/sick/JsApiTest.scala index e7a4468..e8234b1 100644 --- a/json-sick-scala/json-sick/.js/src/test/scala/io/izumi/sick/JsApiTest.scala +++ b/json-sick-scala/json-sick/.js/src/test/scala/io/izumi/sick/JsApiTest.scala @@ -39,6 +39,14 @@ class JsApiTest extends AnyWordSpec { assert(cursor.downField("b").asInt.toOption.contains(2)) assert(cursor.downField("c").asInt.toOption.contains(3)) } + + locally { + val uint8Array = SickJsAPI.encodeJSONStringsToSickUint8Array(js.Dictionary("data" -> """{ "a": 1 , "b": { "c": 2, "d": [3, 4, 5] } }""")) + val reader = SickJsAPI.ebaReaderFromUint8Array(uint8Array, "data") + assert(reader.query("a").asInstanceOf[Int] == 1) + assert(reader.query("b.c").asInstanceOf[Int] == 2) + assert(reader.query("b.d.[1]").asInstanceOf[Int] == 4) + } } } diff --git a/json-sick-scala/json-sick/src/main/scala/izumi/sick/eba/reader/IncrementalEBAReader.scala b/json-sick-scala/json-sick/src/main/scala/izumi/sick/eba/reader/IncrementalEBAReader.scala index 5dafbfe..764ffb2 100644 --- a/json-sick-scala/json-sick/src/main/scala/izumi/sick/eba/reader/IncrementalEBAReader.scala +++ b/json-sick-scala/json-sick/src/main/scala/izumi/sick/eba/reader/IncrementalEBAReader.scala @@ -173,7 +173,7 @@ class IncrementalEBAReader( case currentQuery0 :: next0 => val (currentQuery, next) = handleBracketsWithoutDot(currentQuery0, next0) if (currentQuery.startsWith("[") && currentQuery.endsWith("]")) { - val index = currentQuery.substring(1, currentQuery.length - 2) + val index = currentQuery.substring(1, currentQuery.length - 1) val iindex = index.toInt val resolvedArr = readArrayElementRef(ref, iindex) diff --git a/json-sick-scala/json-sick/src/test/scala/io/izumi/sick/SickCursorTest.scala b/json-sick-scala/json-sick/src/test/scala/io/izumi/sick/SickCursorTest.scala index 3abfaf9..48ddcb3 100644 --- a/json-sick-scala/json-sick/src/test/scala/io/izumi/sick/SickCursorTest.scala +++ b/json-sick-scala/json-sick/src/test/scala/io/izumi/sick/SickCursorTest.scala @@ -183,5 +183,7 @@ class SickCursorTest extends AnyWordSpec { assert(cursor.query("data.person").getValues.get("name").exists(_.asString.contains("Alice"))) assert(cursor.query("data.person").readKey(1).asInt.contains(30)) + + assert(cursor.query("data.numbers.[1]").asString.contains("two")) } } diff --git a/json-sick-scala/npm-template/README.md b/json-sick-scala/npm-template/README.md index 41351d8..708b34c 100644 --- a/json-sick-scala/npm-template/README.md +++ b/json-sick-scala/npm-template/README.md @@ -71,6 +71,10 @@ Accepts dictionary where keys are root names and values are Uint8Arrays containi Accepts an instance of `Uint8Array` and the rootId, returns a cursor to navigate through the structure. +### `ebaReaderFromUint8Array(uint8Array: Uint8Array, rootId: string): EBAReader` + +Accepts an instance of `Uint8Array` and the rootId, returns a reader, which has query method, with jq like requests. + ## License BSD-2-Clause From 204300c12adf8dde8eefcbe06259d1889ce92ec8 Mon Sep 17 00:00:00 2001 From: Yaroslav Sagach Date: Wed, 3 Dec 2025 16:29:24 +0200 Subject: [PATCH 2/2] added test to npm --- .../izumi/sick/eba/reader/EBAReaderJs.scala | 2 +- json-sick-scala/npm-template/test.js | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala index be8439e..3700105 100644 --- a/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala +++ b/json-sick-scala/json-sick/.js/src/main/scala/izumi/sick/eba/reader/EBAReaderJs.scala @@ -21,7 +21,7 @@ class EBAReaderJs(reader: IncrementalEBAReader, rootId: String) { case RefKind.TByte => cursor.asByte case RefKind.TShort => cursor.asShort case RefKind.TInt => cursor.asInt - case RefKind.TLng => cursor.asLong + case RefKind.TLng => cursor.asDouble case RefKind.TBigInt => cursor.asBigInt case RefKind.TFlt => cursor.asFloat case RefKind.TDbl => cursor.asDouble diff --git a/json-sick-scala/npm-template/test.js b/json-sick-scala/npm-template/test.js index 9ad3402..5caf44b 100644 --- a/json-sick-scala/npm-template/test.js +++ b/json-sick-scala/npm-template/test.js @@ -6,7 +6,8 @@ import { encodeObjsToSickUint8Array, encodeJSONStringsToSickUint8Array, encodeJSONBytesToSickUint8Array, - sickCursorFromUint8Array + sickCursorFromUint8Array, + ebaReaderFromUint8Array } from "./json-sick-2.13-fullOpt.js"; test("Encode/Decode obj test", t => { @@ -63,4 +64,18 @@ test("Sick Cursors test", t => { t.is(cursor.downField("arr").downArray.right.value.asString, "b"); t.is(cursor.downField("arr").downArray.downIndex(2).asString, "c"); +}) + +test("EBAReader test", t => { + const encoded = encodeObjToSickUint8Array("data", + { + object: { + life: 42, + array: ["a", "b", "c", "d", "e"] + } + }); + const reader = ebaReaderFromUint8Array(encoded, "data"); + + t.is(reader.query("object.life"), 42); + t.is(reader.query("object.array.[2]"), "c"); }) \ No newline at end of file