Unwrap config settings during the serialisation - java

I try to make config manager for my app. I have some problems with Jackson. Here is my object mapper config:
val mapper = ObjectMapper()
val jackson = mapper.setVisibility(
mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.NONE)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE)
)
.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.enable(SerializationFeature.INDENT_OUTPUT)
and then we saving arraylist of modules.every module has
#JsonProperty("config") public ArrayList<SettingAbstract> settings = new ArrayList<SettingAbstract>( );
SettingAbstract have name and value. i did this
#JsonProperty("property") public abstract String saveCfg();
and then this in SettingAbstract child classes
#Override
public String saveCfg() {
return this.name + ": " + this.value;
}
The result JSON is:
..., {
"enabled" : false,
"name" : "ESP",
"config" : [ {
"property" : "Players: true"
}, {
"property" : "Mobs: false"
}, {
"property" : "Hostiles: false"
} ],
"currentSettingsPage" : 0
}, {
"enabled" : false,
"name" : "Custom Time",
"config" : [ {
"property" : "Ticks: 19000"
} ],
"currentSettingsPage" : 0
}, ...
What I want is:
..., {
"enabled" : false,
"name" : "ESP",
"Players": true",
"Mobs": "false",
"Hostiles": "false",
"currentSettingsPage" : 0
}, {
"enabled" : false,
"name" : "Custom Time",
"Ticks": "19000",
"currentSettingsPage" : 0
}, ...
Main problems:
how to remove theese [] <- from arraylist
how to remove {} <- from SettingAbstract class (because im saving only 1 property)
and how to save key as setting name (various modules has various settings)

You need to unwrap config property. It is a list, so you need to ignore it and add extra method which converts that list to Map. In Kotlin it could look like this:
#JsonAnyGetter
fun getSettingsAsMap(): Map<String, String>? {
return settings.stream().collect(Collectors.toMap(SettingAbstract::name, SettingAbstract::value));
}
And you need to ignore list:
#JsonIgnore public ArrayList<SettingAbstract> settings

Related

RxJava Retrofit make multiple requests for id

I have JSON
{ "cameras" : [
{
"archives" : [
{
"accessPoint" : "xxxx",
"default" : true,
"isEmbedded" : false,
"storage" : "xxxx",
"storageDisplayName" : "xxxx"
}
],
"audioStreams" : [],
"comment" : "",
"detectors" : [
{
"accessPoint" : "xxxx",
"displayName" : "",
"events" : [ "SceneChangeDetected" ],
"isActivated" : true,
"parentDetector" : "",
"type" : "xxxx"
},
{
"accessPoint" : "xxxx",
"displayName" : "",
"events" : [ "xxx" ],
"isActivated" : true,
"parentDetector" : "",
"type" : "xxxx"
}
],
"displayId" : "1",
"displayName" : "xxx",
"ipAddress" : "0.0.0.0",
"isActivated" : true,
"latitude" : "xxxx",
"longitude" : "xxx",
"model" : "Virtual",
"offlineDetectors" : [],
"ptzs" : [],
"textSources" : [],
"vendor" : "AxxonSoft",
"videoStreams" : [
{
"accessPoint" : "xxx
}
]
}, "archives" : [
POJO
public class allCameras {
#SerializedName("cameras")
#Expose
private ArrayList camera;
public ArrayList<Camera> getCameras() {
return camera;
}
public void setCameras(ArrayList<Camera> cameras) {
this.camera = cameras;
}
public class Camera {
#SerializedName("archives")
#Expose
private ArrayList archives;
#SerializedName("displayName")
#Expose
private String displayName;
public ArrayList<Archive> getArchives() {
return archives;
}
public void setArchives(ArrayList<Archive> archives) {
this.archives = archives;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
And I have a code in which I get the List with the help of RXJAVA
Next, I wanted to combine two requests and pass the execution result (id) from one to the other
But I am faced with a problem: in order for this to work, I must receive one Camera object at a time and pass its ID to another function
But I can only get a ArrayList
Help, please, what to do?

Spring Data REST - Unrecognized field "_embedded" by consuming list of entities, Java HATEOAS

I am trying to consume a list of entities from the following REST HAL response:
{
"_embedded" : {
"posts" : [ {
"branch" : 1,
"article" : "aaaaaaa",
"featuredImage" : "aaaaaaa",
"authorId" : 1,
"datePublished" : "2020-05-05T09:11:13.336+0000",
"commentsEnabled" : true,
"enabled" : false,
"views" : 0,
"snippetTitle" : null,
"snippetDescription" : null,
"comments" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8081/posts/1"
},
"post" : {
"href" : "http://localhost:8081/posts/1"
},
"categories" : {
"href" : "http://localhost:8081/posts/1/categories"
}
}
}, {
"branch" : 1,
"article" : "aaaaaaa",
"featuredImage" : "aaaaaaa",
"authorId" : 1,
"datePublished" : "2020-05-05T10:45:15.999+0000",
"commentsEnabled" : true,
"enabled" : false,
"views" : 0,
"snippetTitle" : null,
"snippetDescription" : null,
"comments" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8081/posts/3"
},
"post" : {
"href" : "http://localhost:8081/posts/3"
},
"categories" : {
"href" : "http://localhost:8081/posts/3/categories"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8081/posts/search/byAuthorId?authorId=1&page=0&size=10"
}
},
"page" : {
"size" : 10,
"totalElements" : 3,
"totalPages" : 1,
"number" : 0
}
}
I would like to map these entities to this class:
#Setter
#Getter
#AllArgsConstructor
public class Post {
private int id;
private int branch;
private String article;
private Date datePublished;
private String featuredImage;
private Boolean commentsEnabled;
private Boolean enabled;
private int views;
private String snippetTitle;
private String snippetDescription;
}
However, I keep getting the error:
Unrecognized field "_embedded" (class
org.springframework.hateoas.PagedModel), not marked as ignorable (3
known properties: "links", "page", "content"])
With this code:
ObjectMapper mapper = new ObjectMapper();
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
messageConverter.setObjectMapper(mapper);
ResponseEntity<PagedModel<Post>> responseEntity =
new RestTemplate(Arrays.asList(messageConverter)).exchange(uri, HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<PagedModel<Post>>() {});
The versions are:
Jackson-databind version: 2.11.0
Spring-hateoas version: 1.0.5.RELEASE
Any help would be appreciated!
Response structure seems like PagedResources<T> type.
Use org.springframework.hateoas.PagedResources in ParameterizedTypeReference
ResponseEntity<PagedResources<Post>> responseEntity =
new RestTemplate(Arrays.asList(messageConverter)).exchange(uri, HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<PagedResources<Post>>() {});

swagger core 2.0 disable security for endpoint

I am using Swagger Core 2.0 to generate openAPI 3.0 definition files and
I am having trouble to disable "security" for a particular endpoint.
I have my securitySchemes and root security element defined:
{
"openapi" : "3.0.1",
"security" : [ {
"JWT" : [ ]
} ],
"paths" : {
"/auth" : {
"post" : {
"summary" : "authenticate user",
"operationId" : "authenticate",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/AuthenticationRequest"
}
}
}
},
"responses" : {
"200" : {
"description" : "when user is successfully authenticated",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/AuthenticateUserOutput"
}
}
}
},
"401" : {
"description" : "when email/password not valid or user is blocked/inactive"
}
}
}
},
},
"components" : {
"schemas" : {
"AuthenticateUserOutput" : {
"type" : "object",
"properties" : {
"token" : {
"type" : "string"
},
"lastLoginAt" : {
"type" : "string",
"format" : "date-time"
},
"lastProjectId" : {
"type" : "string"
}
}
},
...,
"AuthenticationRequest" : {
"required" : [ "email", "password" ],
"type" : "object",
"properties" : {
"email" : {
"type" : "string"
},
"password" : {
"type" : "string"
}
}
}
},
"securitySchemes" : {
"JWT" : {
"type" : "http",
"scheme" : "bearer",
"bearerFormat" : "JWT"
}
}
}
}
According to OPEN API 3 spec https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#securityRequirementObject i shall be able to override global "security requirement" for an individual operation. I would like to "disable" JWT security for a few operations and according to https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#securityRequirementObject it can be done:
To remove a top-level security declaration, an empty array can be used.
Unfortunately I am struggling to define "empty security array" on Operation level using annotations...
I tried to specify
security = {}
or
security = #SecurityRequirement(name ="")
but no security element within operation is generated at all....
Any idea ?
Below is my java code (i use for swagger dropwizard integration) that allows one to have SecurityScheme and root level security defined
Info info = new Info()
.title("someTitle")
.description("some description")
.version("1.0")
SecurityScheme jwtSecurity = new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.name("Authorization")
.in(SecurityScheme.In.HEADER)
.scheme("bearer")
.bearerFormat("JWT");
String securitySchemaName = "JWT";
OpenAPI oas = new OpenAPI()
.info(info)
.components(new Components().addSecuritySchemes(securitySchemaName, jwtSecurity))
.addSecurityItem(new SecurityRequirement().addList(securitySchemaName));
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.openAPI(oas)
.prettyPrint(true)
.resourcePackages(Stream.of("my.resources.package")
.collect(Collectors.toSet()));
environment.jersey().register(new OpenApiResource()
.openApiConfiguration(oasConfig));
Then on a few dedicated endpoints i would like to disable security, so i am trying with:
#POST
#Operation(
summary = "authenticate user",
responses = {
#ApiResponse(responseCode = "200", description = "when user is successfully authenticated",
content = #Content(schema = #Schema(implementation = AuthenticateUserOutput.class))),
#ApiResponse(responseCode = "401", description = "when email/password not valid or user is blocked/inactive"),
}
,security = what to put here ?
)
if you want to do it in yml swagger hub style you can put
security: []
in that endpoint after request body, So swagger considers it as no auth for that particular path or endpoint.
According to a comment over on the OpenAPI-Specifiction GitHub project. It should be possible.
Did you try this?
security: [
{}
]
I had the same problem, on a Java SpringBoot webapp (dependency org.springdoc:springdoc-openapi-ui:1.5.2). As per this answer, I solved it adding an empty #SecurityRequirements annotation on the operation. For example:
#POST
#SecurityRequirements
#Operation(
summary = "authenticate user",
responses = {
#ApiResponse(responseCode = "200", description = "when user is successfully authenticated",
content = #Content(schema = #Schema(implementation = AuthenticateUserOutput.class))),
#ApiResponse(responseCode = "401", description = "when email/password not valid or user is blocked/inactive"),
} )
)

Converting Linear json to nested json in mule

I need help in the logic for transforming one json file to another json file. I am trying to achieve this in mule without dataweave in the custom java component.
I want to convert a linear json to nested json, The input data is a linear json containing the details of all files and directory in particular FTP server. The output Json file should be able to nest the files and directory based on the root directory. Here is the example of input and output json.
{
"InputJson": [
{
"type": "dir",
"id": "RootDir",
"name": "abcd",
"Dir": "/abcd"
},
{
"type": "dir",
"name": "Folder1",
"Dir": "/abcd/Folder1",
"id": "XXXXX"
},
{
"type": "file",
"name": "Folder1SubFolder1",
"Dir": "/abcd/Folder1/Folder1SubFolder1",
"id": "XXXXXX"
},
{
"type": "dir",
"name": "Folder2",
"Dir": "/abcd/Folder2"
"id": "XXXXXX"
},
{
"type": "dir",
"name": "Folder2SubFolder1",
"Dir": "/abcd/Folder2/Folder2SubFolder1"
"id": "XXXXXX"
},
{
"type": "file",
"name": "Folder2SubFolder1SubFolder1",
"Dir": "/abcd/Folder2/Folder2SubFolder1/Folder2SubFolder1SubFolder1"
"id": "XXXXXX"
}
]
}
Output
{
"id": "RootDir",
"value": "Files",
"type": "folder"
"OutData": [{
"value": "Folder1",
"OutData": [{
"value": "Folder1SubFolder1"
}
]
}
]
"OutData": [{
"value": "Folder2",
"OutData": [{
"value": "Folder2SubFolder1",
"OutData":[{
"value": "Folder2SubFolder1SubFolder1",
}]
}
]
}
]
the logic
1. CREATE (java) `outputElemtsList` = []
2. FOR EACH (json) `inputElement` IN `InputJson`
3. CREATE (java) `outputElemt`
4. ADD `outputElement` TO `outputElemtsList`
5. IF `outputElement` HAS `parent`
6. ADD `outputElement` TO `parent`.outData
7. CONVERT `outputElemtsList`[0] TO `Json`
assuming, the list in InputJson, is ordered in the same as the sample, (the child never come before their parent)
if not, you'll need to add some checks as:
3. create `outputElement` if not in `outputElemtsList`; else continue
6. create `parent` if not in `outputElemtsList`
in practice
you can use a Json parser, such jakson, to:
// parse InputJson, to Java Objects
Map<String, Object> rootNode = mapper.readValue(jsonString, Map.class);
// ... implement the logic ...
// serialize a java Object into Json
String outputJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(routOutputs);
the code
updated to distinguish between file and folders(dir)
1- OutputElement class
public class OutputElement {
String id, value, type;
public void addOutData(OutputElement outputElement) {}
// constructor, accessors ...
}
1.2- class OutputDir extends OutputElement
public class OutputElement {
List<OutputElement> outData = new ArrayList<>();
#Override
public void addOutData(OutputElement outputElement) {
this.outData.add(outputElement);
}
}
2- Main class : LinearToNestedJson
method to check if outputElements List contains an outputElement
public static boolean contains(List<OutputElement> outputElements, String value) {
for (OutputElement outputElement : outputElements) {
if (outputElement.getValue().equals(value))
return true;
}
return false;
}
main method
public static void main(String args[]) {
JacksonTester tester = new JacksonTester();
try {
ObjectMapper mapper = new ObjectMapper();
String jsonString = IN_JSON;
#SuppressWarnings("unchecked")
Map<String, Object> rootNode = mapper.readValue(jsonString, Map.class);
#SuppressWarnings("unchecked")
List<Map<String, Object>> inputElemnts =
(ArrayList<Map<String, Object>>) rootNode.getOrDefault("InputJson", null);
List<OutputElement> outputElements = new ArrayList<>();
for (Map inputElemnt : inputElemnts) {
String fullpath = (String) inputElemnt.get("Dir");
String[] tree = fullpath.substring(1).split("/");
final int deepth = tree.length;
String dirName = tree[deepth - 1];
final String value = (String) inputElemnt.get("name");
final String id = (String) inputElemnt.get("id");
String type = (String) inputElemnt.get("type");
OutputElement outputElement;
if (type != null && type.equals("dir")) {
outputElement = new OutputDir();
} else {
if(type==null) type = "file";
outputElement = new OutputElement();
}
outputElement.setValue(value);
outputElement.setId(id);
outputElement.setType(type);
if (!contains(outputElements, value)) {
outputElements.add(outputElement);
}
if (deepth > 1) {
String parentName = tree[deepth - 2];
for (OutputElement element : outputElements) {
if (element.getValue().equals(parentName)) {
element.addOutData(outputElement);
}
}
}
// for (int i = 0; i < deepth -1; i++) {
// System.out.println(tree[i]);
// }
}
OutputElement routOutputs = outputElements.get(0);
String outputJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(routOutputs);
System.out.println(outputJson);
} catch (JsonParseException | JsonMappingException e) {
} catch (IOException e) {
}
}
it's output, for the given input (after validation)
{
"id" : "RootDir",
"value" : "abcd",
"type" : "dir",
"outData" : [ {
"id" : "XXXXX",
"value" : "Folder1",
"type" : "dir",
"outData" : [ {
"id" : "XXXXXX",
"value" : "Folder1SubFolder1",
"type" : "file"
} ]
}, {
"id" : "XXXXXX",
"value" : "Folder2",
"type" : "dir",
"outData" : [ {
"id" : "XXXXXX",
"value" : "Folder2SubFolder1",
"type" : "dir",
"outData" : [ {
"id" : "XXXXXX",
"value" : "Folder2SubFolder1SubFolder1",
"type" : "file"
} ]
} ]
} ]
}
Use groupBy - that's exactly what you need.
Here is code:
%dw 1.0
%output application/json
---
items : payload.InputJson groupBy $.id pluck {
id: $$,
values: $
}
Here is result:

How to consume Spring HATEOAS REST resources containing links to another resources?

I have /studentCourses endpoint on the server (built with Spring Data REST) which returns the following content:
{
"_embedded" : {
"studentCourses" : [
{
"uid" : "5f23abe9-b24e-4e76-86b0-d539950a0a41",
"registrationDate" : "7/23/2016",
"_links" : {
"self" : {
"href" : "http://localhost:8080/studentCourses/5f23abe9-b24e-4e76-86b0-d539950a0a41"
},
"studentCourse" : {
"href" : "http://localhost:8080/studentCourses/5f23abe9-b24e-4e76-86b0-d539950a0a41"
},
"course" : {
"href" : "http://localhost:8080/studentCourses/5f23abe9-b24e-4e76-86b0-d539950a0a41/course"
},
"student" : {
"href" : "http://localhost:8080/studentCourses/5f23abe9-b24e-4e76-86b0-d539950a0a41/student"
}
}
},
{
...
},
...
]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/studentCourses"
},
"profile" : {
"href" : "http://localhost:8080/profile/studentCourses"
}
},
"page" : {
...
}
}
And the following client code:
class StudentCourseDTO {
String uuid;
String registrationDate;
StudentDTO student; // contains uuid, firstName, lastName, etc.
CourseDTO course; // contains uuid, name, etc.
// getters, setters
}
RestTemplate restTemplate() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jackson2HalModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MappingJackson2HttpMessageConverter messageConverter =
new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(objectMapper);
messageConverter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON));
return new RestTemplate(Arrays.asList(messageConverter));
}
...
Collection<StudentCourseDTO> studentCourses = restTemplate().exchange(
"http://localhost:8080/studentCourses",
HttpMethod.GET, null,
new ParameterizedTypeReference<PagedResources<StudentCourseDTO>>() {})
.getBody().getContent();
The problem is that StudentCourseDTO.student and StudentCourseDTO.course are always null, but StudentCourseDTO.uuid and StudentCourseDTO.registrationDate are retrieved correctly from the server.
Anyone has an idea what I have missed?
I think there must be someway to tell RestTemplate to automatically follow the links in the returned content like student and course in the example above, but I haven't found a way to do this.
Just because there are links that does not mean they are automatically followed.
I would change the StudentCourseDTO to:
class StudentCourseDTO {
String uuid;
String registrationDate;
}
And then you would deserialize the response to a
PagedResources<Resource<StudentCourseDTO>> studentCourses = restTemplate().exchange(
"http://localhost:8080/studentCourses",
HttpMethod.GET, null,
new ParameterizedTypeReference<PagedResources<Resource<StudentCourseDTO>>>() {})
.getBody().getContent();
For each Resource<StudentCourseDTO> you can then follow the links for studentand course, e.g. by using the RestTemplate to retrieve the resources.
Of course this gives you two additional calls per response item - but the only way to avoid this is to change the service to embed this information in the list resource.

Categories

Resources