{productid: 456813, token: 6t90jwawirc3dznbn8khevkg1b6n7vnx, quote_id: 813, specData: {{super_attribute: {218: {value: 297}}, qty: 1}, {super_attribute: {218: {value: 298}}, qty: 1}, {super_attribute: {218: {value: 299}}, qty: 1}, {super_attribute: {218: {value: 300}}, qty: 1}, {super_attribute: {218: {value: 301}}, qty: 1}}}
print(jsonEncode(params));
Got the following error
Converting object to an encodable object failed: Instance of '_CompactLinkedHashSet<dynamic>'
Your data structure is not valid for directly converting to JSON. The main problem is that you have a Set in params (specData) which is not allowed for jsonEncode which is documented with:
If value contains objects that are not directly encodable to a JSON string (a value that is not a number, boolean, string, null, list or a map with string keys), the toEncodable function is used to convert it to an object that must be directly encodable.
There are two solutions here. You can either change specData to be a List. Or you can use the toEncodable argument to jsonEncode to change the type of your Set to List when converting to JSON:
print(jsonEncode(params, toEncodable: (object) {
if (object is Set) {
return object.toList();
} else {
throw Exception('Could not encode: $object');
}
}));
Bonus: It is difficult to know if your key's in super_attribute is int or String. But you must use String if you want to convert to JSON. If it is int you can also use the toEncodable to fix that but it will get a little messy so it is best if you can get some control over your data structure.
Related
I have a json structure that I need to (sort of) flatten when serializing it into an object. Some of the elements are at the top level and some are in a sub field. In addition, 1 of the fields is an array of space delimited strings that I need to parse and represent as myString.splig(" ")[0]
So, short of a when expression to do the job, can I use something like a jsonpath query to bind to certain fields? I have thought of even doing some kind of 2-pass binding and then merging both instances.
{
"key": "FA-207542",
"fields": {
"customfield_10443": {
"value": "TBD"
},
"customfield_13600": 45,
"customfield_10900": {
"value": "Monitoring/Alerting"
},
"customfield_10471": [
"3-30536161871 (SM-2046076)"
],
"issuetype": {
"name": "Problem Mgmt - Corrective Action"
},
"created": "2022-08-11T04:46:44.000+0000",
"updated": "2022-11-08T22:11:23.000+0000",
"summary": "FA | EJWL-DEV3| ORA-00020: maximum number of processes (10000) exceeded",
"assignee": null
}
}
And, here's the data object I'd like to bind to. I have represented what they should be as jq expressions.
#Serializable
data class MajorIncident constructor(
#SerialName("key")
val id: String, // .key
val created: Instant, // .fields.created
val pillar: String, // .fields.customfield_10443.value
val impactBreadth: String?,
val duration: Duration, // .fields.customfield_13600 as minutes
val detectionSource: String, //.fields.customfield_10900.value
val updated: Instant, // .fields.updated
val assignee: String, // .fields.assignee
// "customfield_10471": [
// "3-30536161871 (SM-2046076)"
// ],
val serviceRequests: List<String>?, // .fields.customfield_10471 | map(split(" ")[0]) -
#SerialName("summary")
val title: String, //.summary
val type: String, // .fields.issuetype.name // what are options?
)
If you're using Kotlinx Serialization, I'm not sure there is any built-in support for jsonpath.
One simple option is to declare your Kotlin model in a way that matches the JSON. If you really want a flattened object, you could convert from the structured model into the flat model from Kotlin.
Another option is to write a custom serializer for your type.
Why does the following snippet not compile, and how is the ES6 JSON interface supposed to be used?
let myVar: JSON = {"id": "12"};
Gives the following error message: Type '{ id: string; }' is not assignable to type 'JSON'. Object literal may only specify known properties, and '"id"' does not exist in type 'JSON'.
My IDE gives the following definition for JSON, but I can’t understand it:
interface JSON {
readonly [Symbol.toStringTag]: string;
}
JSON is a global object defined by the JS specification designed to hold the parse and stringify methods for converting between JS data structures and JSON texts.
It isn't a type. It isn't supposed to be used as one.
When creating a custom object format, you are supposed to define your own type (although it isn't useful to do so here, but might be if you define a function elsewhere that you need to pass the object to as an argument). When dealing with JSON, you are dealing with strings.
type MyFormat = {
id: string;
}
let myVar: MyFormat = {"id": "12"};
let myJSON: string = JSON.stringify(myVar);
There's no reason you'd use the JSON interface in your code. It relates to the JSON built-in object, and there's no reason to use that object other than using its parse and/or stringify methods to parse or create JSON text.
From your code, you appear to misunderstand what JSON is. (Lots of people do! :-) ) JSON is a textual notation for data exchange. (More here.) If you're dealing with JavaScript or TypeScript source code, and not dealing with a string, you're not dealing with JSON.
Your myVar refers to an object. There's no need to put a type annotation on it, you can just let TypeScript infer it from the initializer, but if you wanted to put a type annotation on it you'd use either {id: string;} or Record<string, string> or some other object type:
// Letting TypeScript infer
let myVar = {"id": "12"};
// Or specify an object with an `id` property of type string
let myVar: {id: string;} = {"id": "12"};
// Or create a type alias and use it
type MyVarType = {
id: string;
};
let myVar: MyVarType = {"id": "12"};
// Or perhaps an object where any string is a valid property name and the types are all strings
let myVar: Record<string, string> = {"id": "12"};
See the documentation linked above for more about object types.
Side note: If you meant to use a number for id, you'd use id: number or similar:
let myVar: {id: number;} = {id: 12};
Background: Project uses Yelp API in JSON to sift through business data. Two types of endpoints are use - https://www.yelp.com/developers/documentation/v3/business_search and https://www.yelp.com/developers/documentation/v3/business. The first part of this projects works as expected where the business_search api is parsed, saved as a list of strings and converted to a Business objects when it is needed to be displayed. The JSON -> Object code is as follows:
factory Business.fromJSON(Map<String, dynamic> json) {
return Business(
rating: json['rating'],
price: json['price'],
phone: json['phone'],
id: json['id'],
name: json['name'],
alias: json['alias'],
isClosed: json['is_closed'],
reviewCount: json['review_count'],
url: json['url'],
imageUrl: json['image_url'],
);
}
The code works when a call is converted from the business/search endpoint, with the JSON printout being:
json = {rating: 4.5, price: $$, phone: +16316751500, id: D5dS2-8JXhZxo3BzMLV3xQ, name: Ichi sushi & ramen, alias: ichi-sushi-and-ramen, is_closed: false, review_count: 128, url: https://www.yelp.com/biz/ichi-sushi-and-ramen?adjust_creative=Aj73ii92JG9IDLNvRyn_Ow&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=Aj73ii92JG9IDLNvRyn_Ow, image_url: https://s3-media1.fl.yelpcdn.com/bphoto/Phib_AwTkdD_kBV8r7179Q/o.jpg}
The Issue: I encounter the following exception
[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: FormatException: Unexpected character (at character 2)
{id: D5dS2-8JXhZxo3BzMLV3xQ, alias: ichi-sushi-and-ramen...
^
when this response from business/details is called, saved and attempted to be converted to objects:
json = {id: D5dS2-8JXhZxo3BzMLV3xQ, alias: ichi-sushi-and-ramen, name: Ichi sushi & ramen, image_url: https://s3-media1.fl.yelpcdn.com/bphoto/Phib_AwTkdD_kBV8r7179Q/o.jpg, is_claimed: true, is_closed: false, url: https://www.yelp.com/biz/ichi-sushi-and-ramen?adjust_creative=Aj73ii92JG9IDLNvRyn_Ow&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_lookup&utm_source=Aj73ii92JG9IDLNvRyn_Ow, phone: +15836751500, display_phone: (583) 675-1500, review_count: 128, categories: [{alias: sushi, title: Sushi Bars}, {alias: ramen, title: Ramen}], rating: 4.5, location: {address1: 200 Country Rd, address2: null, address3: , city: Bayshore, zip_code: 11803, country: US, state: NY, display_address: [200 Country Rd, Bayshore, NY 11803], cross_streets: }, coordinates: {latitude: 40.92842, longitude: -73.116}, photos: [https://s3-media1.fl.yelpcdn.com/bphoto/Phib_AwTkdD_kBV8r7179Q/o.jpg, https://s3-media2.fl.yelpcdn.com/bp<…> (*My note: debugger truncates with <...>*)
What is going wrong here? I have been trying many (and I mean many) different cases to rule out what the issue is but cannot find anything that is blatantly amiss. The only difference I can see is that the first type of call (business/search) starts with "rating" and contains fewer fields while the second type of call (business/details) returns much more data (note that I do not need much of it and hence do not need it in the Business object, but the nature of Yelp's api requires I do it in this way) and starts with the "id" field instead. Are these differences to blame and what does this error mean? Can you "plug in" a JSON to an object that has more fields than needed properties as I would like to keep the Business object as versatile as possible? Regardless, how could this be resolved? If more info is needed, please let me know. Thank you for your help and time!
After further analyzing the code, #KhashayarMotarjemi's suggestion tipped me off on a possible problem: the way the data was encoded. To save the data, I ran the the JSON through the toString() method, stripping the data of all of its " " and making it unable to be correctly parsed as JSON later on when retrieved from memory. The correct method would be jsonEncode(yourData) as this converts it to a string to be saved, but preserves the format needed when decoded down the line. So to anyone reading, if you are trying to store your JSON as a string, you must use jsonEncode() and not toString() when doing so. I hope this can help someone in the future!
I am trying to deserialize some crypto exchange JSON. The structure of the JSON is predictable but the keys are not. Each time the server adds a new currency it becomes a new key. For example, it looks something like this:
{
"timestamp":"1562405",
"username":"1234",
"BTC":
{"available":"0.00","orders":"0.00000000"},
"BCH":
{"available":"0.0000000","orders":"0.00000000"},
..
..
"GUSD":
{"available":"0.00","orders":"0.00"}
}
I tried the usual approach of defining a struct to deserialize into but every time there is a new currency my program will have an error.
I thought I would just read it all into a Value and then just iterate over it manually to put it all into a struct.
let balance_data: Value = serde_json::from_str(&String::from_utf8_lossy(&body)).unwrap();
println!("balance_data: {:?}", balance__data);
for element in balance_data.iter() {
//push into a proper map...
}
But I can't:
no method named `iter` found for type `serde_json::Value`.
I thought I would put it into a Vec<Value> but this causes a panic:
thread 'main' panicked at 'called ``Result::unwrap()` on an `Err` value: Error("invalid type: map, expected a sequence", line: 1, column: 0)'`
I also experimented with reading it into a HashMap but couldn't quite seem to crack it.
It would be amazing if we could do something like:
let balance_data: RecvBalance = serde_json::from_str(&String::from_utf8_lossy(&body)).unwrap();
#[derive(Debug, Deserialize)]
struct RecvBalance {
timestamp: String,
username: String,
HashMap<String, RecvBalanceData>,
}
#[derive(Debug, Deserialize)]
struct RecvBalanceData {
available: String,
orders: String,
}
Has anyone dealt with this situation? I need a struct with the balance data in it that I can lookup later in my program.
The doc has a related example. You need to put the HashMap and use flatten attribute:
#[derive(Debug, Deserialize)]
struct RecvBalance {
timestamp: String,
username: String,
#[serde(flatten)]
moneys: HashMap<String, RecvBalanceData>,
}
While trying to learn JSON-parsing in Scala using Play Framework's ScalaJson through JSON automated mapping using case classes, I'm getting the said error.
com.fasterxml.jackson.core.JsonParseException: Unexpected character (':' (code 58)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source:
"column" : {
"name" : "column_name"
}; line: 2, column: 11]
at com.fasterxml.jackson.core.JsonParser._constructError(scalaPlayJson.sc:1577)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(scalaPlayJson.sc:529)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(scalaPlayJson.sc:458)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(scalaPlayJson.sc:1620)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(scalaPlayJson.sc:685)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(scalaPlayJson.sc:175)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(scalaPlayJson.sc:124)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(scalaPlayJson.sc:119)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(scalaPlayJson.sc:3704)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(scalaPlayJson.sc:2001)
at play.api.libs.json.jackson.JacksonJson$.parseJsValue(scalaPlayJson.sc:231)
at play.api.libs.json.StaticBinding$.parseJsValue(scalaPlayJson.sc:12)
at play.api.libs.json.Json$.parse(scalaPlayJson.sc:167)
at #worksheet#.#worksheet#(scalaPlayJson.sc:50)
The input in question is the simplest JSON one could think of [file: column.json]
{
"column": {
"name": "column_name"
}
}
I'm using the following code to parse the given Json file.
case class Column(
tableId: Option[Int] = None,
id: Option[Int] = None,
name: String,
shouldCopy: Option[Boolean] = None,
dataType: Option[String] = None,
defaultValue: Option[String] = None
) {
def showColumnInfo: Unit = println(s"Name = $name")
}
object Column {
implicit val reads = Json.reads[Column]
implicit val writes = Json.writes[Column]
implicit val format = Json.format[Column]
}
Json.parse(Source.fromFile(s"$path/column.json").mkString("")).
asOpt[Column].
getOrElse(Column(name = "Could not read")).
showColumnInfo
Things that I've tried without success:
Change the key "column" to "Column" in JSON (capitalize 'C' to match case class's name)
Provide the above JSON as String instead of reading it from file
My questions are:
What's causing the error?
Where can I find list of all JsonParseException error codes with their meanings?
Framework versions:
Scala v2.11.11
Play-Json v2.6.6 [SBT: "com.typesafe.play" %% "play-json" % "2.6.6"]
SBT v1.0.3
Turns out my input JSON was wrong (it was a valid JSON, but incorrect as per provided case class). I had wrapped it inside an extra pair of braces {} where it should have been
{
"name": "column_name"
}
For anyone starting with Play-JSON, I would suggest them to use JsValue.as[T] instead of JsValue.asOpt[T] because the latter doesn't report any errors and you'll keep banging your head (I wasted over 5 hours :-(). The docs warn about it in advance:
Although the asOpt method is safer, any error information is lost.