I'm trying to parse json into kotlin objects but the problem is that its key fields are numbers any idea how can parse them , I've tried serialized name but still facing problem.
The json response looks like this :
{
"Id": [{
"1": {
"name": "name1",
"class": "11a"
}
},
{
"2": {
"name": "name2",
"class": "11b"
}
}
]
}
I'm using gson and the main thing i'm trying to do is to store this number fields as some other string objects.
You can parse them into a list of maps, then "map" those to your data classes instead:
val input = """{
"Id": [{
"1": {
"name": "name1",
"class": "11a"
}
},
{
"2": {
"name": "name2",
"class": "11b"
}
}
]
}"""
val gson = Gson()
val parsed: Map<String, List<Map<String, Any>>> =
gson.fromJson(input, (object : TypeToken<Map<String, List<Map<String, Any>>>>(){}).type)
println(parsed["Id"]?.get(0)?.get("1")) // {name=name1, class=11a}
It will have some nasty generic signature, though.
If you're working with Kotlin, take a look at Klaxon, it will improve your experience.
Related
I want to remove deep empty json array from my json before/during processing by circe.
Incoming JSON
{
"config": {
"newFiles": [{
"type": "audio",
"value": "welcome1.mp3"
}],
"oldFiles": [],
"channel": "BC"
}
}
or
{
"config": {
"newFiles": [],
"oldFiles": [{
"type": "audio",
"value": "welcome1.mp3"
}],
"channel": "BC"
}
}
Resulted Json should look like
{
"config": {
"newFiles": [{
"type": "audio",
"value": "welcome1.mp3"
}],
"channel": "BC"
}
}
or
{
"config": {
"oldFiles": [{
"type": "audio",
"value": "welcome1.mp3"
}],
"channel": "BC"
}
}
What i understand that this can be done before decoding config as well as during decoding config.
The idea here is i want to handle only one of files (either new or old) at my case class level.
Method 1: Tried at config decoding level which works well.
case class File(`type`: String, value: String)
case class Config(files: List[File],
channel: String = "BC")
object Config{
implicit final val FileDecoder: Decoder[File] = deriveDecoder[File]
implicit val ConfigDecoder: Decoder[Config] = (h:HCursor) =>
for {
oldFiles <- h.get[List[File]]("oldFiles")
files <- if (oldFiles.isEmpty) h.get[List[File]]("newFiles") else h.get[List[File]]("oldFiles")
channel <- h.downField("channel").as[String]
}yield{
Config(files, channel)
}
}
case class Inventory(config: Config)
object Inventory {
implicit val InventoryDecoder: Decoder[Inventory] = deriveDecoder[Inventory]
}
Method 2: Tried before feeding into decoding which didn't worked out
Let me know what could be the elegant approach to handle it.
PS: I have multiple similar config decoders and if i handle this at config decoding level then there will be a lot of boiler plate code.
I have simplified the problem a little bit again, but combining this with the previous answer should be simple.
I also took advantage of cats.data.NonEmptyList
final case class Config(files: NonEmptyList[String], channel: String = "BC")
object Config {
implicit final val ConfigDecoder: Decoder[Config] =
(
(Decoder[NonEmptyList[String]].at(field = "newFiles") or Decoder[NonEmptyList[String]].at(field = "oldFiles")),
Decoder[String].at(field = "channel")
).mapN(Config.apply).at(field = "config")
}
This can be used like this:
val data =
"""[
{
"config": {
"newFiles": ["Foo", "Bar", "Baz"],
"oldFiles": [],
"channel": "BC"
}
},
{
"config": {
"newFiles": [],
"oldFiles": ["Quax"],
"channel": "BC"
}
}
]"""
parser.decode[List[Config]](data)
// res: Either[io.circe.Error, List[Config]] =
// Right(List(
// Config(NonEmptyList("Foo", "Bar", "Baz"), "BC"),
// Config(NonEmptyList("Quax"), "BC")
// ))
Note: I am assuming that at least one of the two lists must be non-empty and give priority to the new one.
You can see the code running here.
I have an issue with a JSON file, which is a list of country objects, as below:
{
"Countries": [
{
"Code": "AFG",
"Name": "Afghanistan",
"Population": 38928346
},
{
"Code": "ALA",
"Name": "Åland Islands",
"Population": 28007
},
{
"Code": "ALB",
"Name": "Albania",
"Population": 2877797
},
{
"Code": "DZA",
"Name": "Algeria",
"Population": 43851044
},
{
"Code": "ASM",
"Name": "American Samoa",
"Population": 55191
}
]
}
I am trying to use this code to read it and deserialize it into a List object:
Stream? countriesResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyProject.Countries.json");
if (countriesResourceStream == null)
{
return;
}
var countries = new List<Country>();
using (StreamReader reader = new StreamReader(countriesResourceStream))
{
var serializer = new JsonSerializer();
countries = serializer.Deserialize<List<Country>>(new JsonTextReader(reader));
}
However the serializer.Deserialize method throws the exception:
'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyProject.Models.EntityFramework.Country]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
What is the issue with my JSON? I have tried both Newtonsoft and System.Text.Json.
The problem is that your JSON does not represent a list of countries, it represents an object that contains a list of countries. You need another class:
class CountryListContainer
{
public List<Country> Countries { get; set; }
}
Deserialize into the container class and then you can get your country list from that:
using (StreamReader streamReader = new StreamReader(countriesResourceStream))
using (JsonTextReader jsonReader = new JsonTextReader(streamReader))
{
var serializer = new JsonSerializer();
countries = serializer.Deserialize<CountryListContainer>(jsonReader).Countries;
}
Fiddle: https://dotnetfiddle.net/5DM4il
Alternatively, you could change your JSON as #Charles Duffy suggested in the comments. If the JSON looked like this (without the outer object) then your existing code would work:
[
{
"Code": "AFG",
"Name": "Afghanistan",
"Population": 38928346
},
{
"Code": "ALA",
"Name": "Åland Islands",
"Population": 28007
},
{
"Code": "ALB",
"Name": "Albania",
"Population": 2877797
},
{
"Code": "DZA",
"Name": "Algeria",
"Population": 43851044
},
{
"Code": "ASM",
"Name": "American Samoa",
"Population": 55191
}
]
Fiddle: https://dotnetfiddle.net/gMHhcX
I want to transform my data from one json structure to another. What is the best way to do it?
Here is my original resource (customer) structure is:
{
"id": "123",
"data": {
"name": "john doe",
"status": "active",
"contacts": [
{
"email": "john#email.com"
},
{
"phone": "12233333"
}
]
}
}
I want to change it to:
{
"id": "123",
"name": "john doe",
"status": "active",
"contacts": [
{
"email": "john#email.com"
},
{
"phone": "12233333"
}
]
}
Keeping in mind that I might have an array of resources(customers) being returned in GET /customers cases. I want to change that to an array of new data type.
If customer object is array of object then below will help you to get desire format result
var result = customerObj.map(x => {
return {
id: x.id,
name: x.data.name,
status: x.data.status,
contacts: x.data.contacts
};
});
here I have used Object.assign() it will be helpful to you
var arr={
"id": "123",
"data": {
"name": "john doe",
"status": "active",
"contacts": [
{
"email": "john#email.com"
},
{
"phone": "12233333"
}
]
}
}
arr=Object.assign(arr,arr.data);
delete arr['data'];
console.log(arr);
You have to Json.parse the json into variable, and then loop through the array of objects, changes the object to the new format, and then JSON.stringify the array back to json.
Example code
function formatter(oldFormat) {
Object.assign(oldFormat, oldFormat.data);
delete oldFormat.data;
}
let parsedData = JSON.parse(Oldjson);
//Take care for array of results or single result
if (parsedData instanceof Array) {
parsedData.map(customer => formtter(customer));
} else {
formatter(parsedData);
}
let newJson = JSON.stringify(parsedData);
console.log(newJson);
Edit
I made the formatter function cleaner by using Kalaiselvan A code
I have a json string. I need to get a specific object based on an id value. Suppose I entered 2, then I want {"id":"2","name":"def"} as the result. I want this to be done in java class.
[
{"id":"1",
"name":"abc"},
{"id":"2",
"name":"def"}
]
Put the Objects in the Array for better manipulation..!!!
JSONObject data = new JSONObject(YOUR_JSON);
JSONArray data_Values=data.getJSONArray(values);
int n=2;// Entered ID
for(int i=0;i<=data_Values.length();i++)
{
if(n==data_Values.getInt("id"))
{
id=data_Values.getInt("id");
name=data_Values.getString("name");
}
}
JSON Data
{
"Values": [
{
"id": "1",
"name": "ABC"
},
{
"id": "2",
"name": "EFG"
},
{
"id": "3",
"name": "HIJ"
}
]
}
im trying to extract my data from json into a case class without success.
the Json file:
[
{
"name": "bb",
"loc": "sss",
"elements": [
{
"name": "name1",
"loc": "firstHere",
"elements": []
}
]
},
{
"name": "ca",
"loc": "sss",
"elements": []
}
]
my code :
case class ElementContainer(name : String, location : String,elements : Seq[ElementContainer])
object elementsFormatter {
implicit val elementFormatter = Json.format[ElementContainer]
}
object Applicationss extends App {
val el = new ElementContainer("name1", "firstHere", Seq.empty)
val el1Cont = new ElementContainer("bb","sss", Seq(el))
val source:String=Source.fromFile("src/bin/elementsTree.json").getLines.mkString
val jsonFormat = Json.parse(source)
val r1= Json.fromJson[ElementContainer](jsonFormat)
}
after running this im getting inside r1:
JsError(List((/elements,List(ValidationError(List(error.path.missing),WrappedArray()))), (/name,List(ValidationError(List(error.path.missing),WrappedArray()))), (/location,List(ValidationError(List(error.path.missing),WrappedArray())))))
been trying to extract this data forever, please advise
You have location instead loc and, you'll need to parse file into a Seq[ElementContainer], since it's an array, not a single ElementContainer:
Json.fromJson[Seq[ElementContainer]](jsonFormat)
Also, you have the validate method that will return you either errors or parsed json object..