Is it possible to set value for jsonpaths
ilkerc opened this issue · comments
just as on com.jayway.jsonpath.DocumentContext.set()
, are there any implementations for settings a value for given jsonpath ?
/**
* Set the value a the given path
*
* @param path path to set
* @param newValue new value
* @return a document context
*/
DocumentContext set(JsonPath path, Object newValue);
There is not, but I can add it. Probably this weekend.
@ilkerc v0.4.0 is up and now supports this.
@KittyMac works quite well thanks!. I did implement most of my use cases but the following;
let json = #"{"someValue": ["elem1", "elem2", "elem3"]}"#
let someValue = #"["elem4", "elem5"]"#
let newValue: String? = json.parsed { root in
root?.query(map: "$.someValue", { _ in
// ignore callback's jsonelement, return mine
let newValue: JsonElement? = Spanker.parsed(string: someValue, { $0 })
return newValue
})
return root?.description
}
print(newValue)
The problem is array of strings can't be parsed by Spanker
I think the problem is with the parser let newValue: JsonElement? = Spanker.parsed(string: someValue, { $0 })
I checked on callback; the returned JsonElement is not the same as the one on the callback.
I think this might be related with HalfHitch.using
, or am I missing something?
This can be more easily implemented like this:
let oldJson = #"{"someValue": ["elem1", "elem2", "elem3"]}"#
let newJson: String? = oldJson.query(map: "$.someValue", {_ in
return ["elem4", "elem5"]
} ) { root in
return root.description
}
XCTAssertEqual(newJson, #"{"someValue":["elem4","elem5"]}"#)
The return value from your map callback is auto-converted to a JsonElement for you, so you can return any json-like-thing and it should handle it. This is more costly performance-wise (since it needs to use as? to determine what the thing is at runtime), but should be fine unless you discover it is too slow for your use case.
Spanker's performance comes from making a hierarchy of JsonElements who reference the memory in the original storage (like a bunch of SubStrings referencing a single String, these lightweight objects are cheap to make so long as they don't need to make copies). This is the reason for the .parsed() { root in ... }, as the source data is only guaranteed to be live inside of it.
If you wanted to do the above more performantly, then we want to move the creation of the JsonElement outside of the map. Like this:
let oldJson = #"{"someValue": ["elem1", "elem2", "elem3"]}"#
let replacementElement = JsonElement(unknown: ["elem4", "elem5"])
let newJson: String? = oldJson.query(map: "$.someValue", {_ in
return replacementElement
} ) { root in
return root.description
}
XCTAssertEqual(newJson, #"{"someValue":["elem4","elem5"]}"#)
If you really want to create the element from a string, you can indeed use Spanker like so:
let oldJson = #"{"someValue": ["elem1", "elem2", "elem3"]}"#
let replacementJson = #"["elem4", "elem5"]"#
let newJson: String? = replacementJson.parsed { replacementElement in
return oldJson.query(map: "$.someValue", {_ in
return replacementElement
} ) { root in
return root.description
}
}
XCTAssertEqual(newJson, #"{"someValue":["elem4","elem5"]}"#)