Pick any element from JSON schema while streaming data to parse - jackson

I'm writing a de-serializer which reads a huge json file and puts records matching a filter (logic in my application) into database. The json file has a fixed schema as follows:
{
"cityDetails": {
"name": "String",
"pinCodes": "Array of integers ",
"people": [{
"name": "String",
"age": "Integer"
}]
}
}
I am only interested in streaming list of "people" from the file. I am aware that GSON/Jackson provide streaming APIs which I can use but I want to avoid looping through the tokens as I stream them and match their name to see if I am interested in them. I believe that there should be a solution which can do the streaming in background and point/seek the stream to the token I am interested in. I don't see any reason why this should not be possible if I provide my JSON schema. Is there are solution available for this?
Here's a sample instance of my JSON:
{
"cityDetails": {
"name": "mumbai",
"pinCodes": ["400001", "400002"],
"people": [{
"name": "Foo",
"age": 1
}, {
"name": "Bar",
"age": 2
}]
}
}

With GSON I would just create corresponding DTOs for the data to be parsed.
So you have some wrapper that is the root object:
#Getter
public class Wrapper {
private CityDetails cityDetails;
}
and city details:
#Getter
public class CityDetails {
private List<Person> people;
}
and possibly many Persons in the list people:
#Getter
#ToString
public class Person {
private String name;
private Integer age;
}
Then you can simply use for example Reader like below:
#Test
public void test() {
Gson gson = new Gson();
// assuming your json is named "test.json" in the same directory as test
Reader r = new InputStreamReader(getClass().getResourceAsStream("test.json"));
Wrapper wrapper = gson.fromJson(r, Wrapper.class);
wrapper.getCityDetails().getPeople().forEach(p -> log.info("{}", p.toString()));
}
Gson will search and instantiate only what is specified in DTO-classes the rest is ignored when parsing.

A nice way of doing this would be to use JsonPath.
A json path of:
$.cityDetails.people
will return just the contents of the people array:
[
[
{
"name": "Foo",
"age": 1
},
{
"name": "Bar",
"age": 2
}
]
]
Here is a Java implementation...

Related

mule 4 payload inside java

I need to access the mule 4 payload inside java code in my mule application.
my payload is a complex json (nested json) with many fields so I cannot create a function with many parameters.
what is the best way of doing that.
my java code is invoked using the java-invoke component (the class/method are not static).
my java function looks like the following:
public class MyClass {
public static void main(String args[]) {
}
public void myMethod(Object payload) {
//do some stuff with payload
}
}
my payload as mentioned is a complex json like below
{
"time": "2022-01-02T11:11:11",
"name": "satish",
"app" : "appname",
childJson: {
"key1": "val1",
"key2": "val2"
},
"anothersubjson: {
"key1": "val1",
"key2": "val2"
"key3" : [{a: "a", b: "b"}]
}
}
my mule code to invoke java is below
<java:new class="com.me.MyClass"
constructor="MyClass()"
target="myClassVar"/>
<java:invoke instance="#[vars.myClassVar]"
class="com.me.MyClass"
method="myMethod(Object)"
target="response">
<java:args>#[{data: payload
}]</java:args>
</java:invoke>

Intellij SSR: how to extract structured information from initializer expressions

Is there a way to extract into some structured format (JSON, XML, whatever) partial information from a number of constructor calls expressions?
Say we have code like:
public enum Model {
TOYOTA,
FORD
}
public enum Dealership {
TOYOTA_GRAND_PRIX(Model.TOYOTA, Set.of("LAND-CRUISER", "AVENSIS")),
FORD_DELUXE(Model.FORD, Set.of("FOCUS"));
private final Model model;
private final Set<String> brands;
}
and I would like to get something like
[
{
"model": "TOYOTA",
"brands": ["LAND-CRUISER", "AVENSIS"]
},
{
"model": "FORD",
"brands": ["FOCUS"]
}
]
is there a way to achieve (something like) this, with SSR?

Spring Hateoas: EntityModel _links rendered before content

This is a weird problem to describe since it's no actually a problem in the technical sense but still makes me curious enough to ask about it:
I created a #RestController that returns ResponseEntity<EntityModel<?>>. I build the EntityModel and attach a self link built with linkTo and methodOn. Now for some reason, the output looks like this:
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/points/knx/office_light"
}
},
"labels" : {
"name" : "Light",
"room" : "Office"
},
"access" : [ "READ", "WRITE" ],
"type" : "SwitchPoint",
"state" : "OFF"
}
Contrary to other rest services I have build, the "_link" gets rendered at the top not at the bottom. Any ideas why?
#GetMapping("{ext}/{id}")
public ResponseEntity<EntityModel<Map<String, Object>>> oneByExt(#PathVariable String ext,
#PathVariable String id) {
EntityModel<Map<String, Object>> point = client.getPoint(ext, id);
return new ResponseEntity<>(localToGlobal(ext, point), HttpStatus.OK);
}
private <T> EntityModel<T> localToGlobal(String ext, EntityModel<T> model) {
ComposedId id = ComposedId.fromEntityModel(ext, model);
Link newSelfLink = linkTo(methodOn(PointController.class).oneByExt(id.getExtension(), id.getIdentifier()))
.withSelfRel();
EntityModel<T> newModel = EntityModel.of(model.getContent());
newModel.add(newSelfLink);
return newModel;
}
It's probably due to the Map, I'm assuming you using something like HashMap which has no guarantee of iteration order. Try change it to a LinkedHashMap and see what happens (should print the values in the order they were added to the map)

Populate data classes with values from linkedhashmap

I have a linkedhashmap that has the following shape: <String, Subject>. The class Subject has the following fields:
class Subject {
var name: Boolean? = null
var lastname: Boolean? = null
var location: Boolean? = null
..
}
final_result =
"admin" -> Subject
"customer" -> Subject
etc.
I need to populate data classes that have the following format:
data class SubjectSummary(
val admin: SubjectData,
val customer: SubjectData
...
)
data class SubjectData(val details: DetailsData)
data class DetailsData(val name:String, val lastName:String ...)
Because I need to serialize the SubjectSummary class and get the following json format:
{
"admin": {
"details": {
"name": "",
"lastname": "",
...
}
}
"customer": {
"details": {
"name": "",
"lastname": "",
...
}
}
}
How do I assign the final_result map to match the SubjectSummary structure? I have done it with simple data classes, but when the fields within the data class are data classes, I'm not sure hot to populate it. Any guidance?
For simplicity I'm only showing a small example with a few fields.
If your goal with this transformation is just to be able to serialize with the given JSON format, then you don't need this SubjectSummary class. A Map<String, SubjectData> would be sufficient and probably more convenient to create when transforming from the initial map.
Also, it seems that DetailsData contains the same fields as Subject. If that's the case there is no need for an extra class either.
So in the end it seems you just need to create a Map<String, SubjectData where SubjectData could be defined as data class SubjectData(val details: Subject). You can transform your initial map pretty easily then:
val transformed = finalResult.mapValues { (_, subject) -> SubjectData(subject) }

Jackson parse JSON into Java Map

I have such JSON:
{
"list": [
{
"product": {
"id": 1123456,
"context": {
}
},
"items": [
]
},
and a code that reads it:
TypeReference<HashMap<String, Object>> typeRef
= new TypeReference<HashMap<String, Object>>() {};
InputStream inputStream = TypeReference.class.getResourceAsStream("/mocks/Docs.json");
Map<String, Object> map = mapper.readValue(inputStream, typeRef);
But I don't want the simple Map<String, Object>, I want to map into a map that looks like Map<String, MyRepresentation> map:
Is there a direct way to do it, or I need first to read it into Map<String, Object> and then manipulate it manually and fill the MyRepresentation object?
the JSON file structure doesn't correspond to the classes at all.
ConditionSummary looks like a type of contextData element accessible as docList[i].product.contextData if it had id, which is defined in the product element.
also, AccountManagerStatistics#map is not public and doesn't have #JsonProperty annotation, so it is out of json for now.
try creating the sample file first if you sure the classes represent the truth:
Map<String, AccountManagerStatistics> map = createStubData();
new ObjectMapper().writerFor(new TypeReference<Map<String, AccountManagerStatistics>>() {}).writeValueAsString(map)
or try to modify your classes to match the data,
which is probably what should be done here.
then you could start from the top and introduce a proper class instead of using Map<String, X>,
BTW no need for HashMap in TypeReference:
//#XmlRootElement
public class Root {
#JsonProperty("docList") //or #XmlElement("docList")
public final List<Doc> docs;
...
#ConstructorProperties({ "docs", ... })
public Root(List<Doc> docs, ...) {
this.docs = List.copyOf(docs);
...
}
}