asyncapi / modelina

A library for generating typed models based on inputs such as AsyncAPI, OpenAPI, and JSON Schema documents with high customization
https://modelina.org
Apache License 2.0
322 stars 185 forks source link

Add helper functions to un/marshal data models for Java #488

Closed jonaslagoni closed 2 years ago

jonaslagoni commented 3 years ago

Reason/Context

We need some helper functions to be able to marshal the data model instances to JSON and unmarshal JSON to instances of the data model.

Description

The reason we need to write this our self is that we add properties to data models which are technically not properties directly, an example here is additionalProperties, as it is rendered as a property in the data model, but when you want to convert an instance of the data model to bytes or JSON for a payload, it should not be used as is but unwrapped.

ritik307 commented 2 years ago

@jonaslagoni I would like to work on this issue

jonaslagoni commented 2 years ago

What is required to solve this is a preset, similar to the one we have for TypeScript. That does not necessarily mean that you have to make the functionality locate in the Java common preset. I even think it makes sense to create a completely new preset such as for Java Jackson preset.

When the preset has been created, we need to create the same documentation and example code as we did for TypeScript. Otherwise how will users know the functionality exist 😄

Let me know if you have any questions 🙂

ritik307 commented 2 years ago

@jonaslagoni Correct me if I am wrong. What I understood is that I have to write CommomPreset.ts for Java, like the one present in the Typescript.

jonaslagoni commented 2 years ago

Correct, at least the part about serialization (also called marshaling in TS): https://github.com/asyncapi/modelina/blob/ffc0cd8673791b262926093e381c17823fbe9565/src/generators/typescript/presets/CommonPreset.ts#L201

ritik307 commented 2 years ago

Hi @jonaslagoni , There is an interface renderer in TypeScript un/marshall.

export interface TypeScriptCommonPresetOptions {
  marshalling: boolean;
  example: boolean;
}

Noob ques: But the interface renderer is missing in Java so do I need to make one for Java or not. I am a bit confused since the code is in TS so if I make an interface renderer for Java won't there be a syntax that is mismatched?

jonaslagoni commented 2 years ago

Noob ques: But the interface renderer is missing in Java so do I need to make one for Java or not. I am a bit confused since the code is in TS so if I make an interface renderer for Java won't there be a syntax that is mismatched?

As there is a huge difference between interfaces in TypeScript vs Java because TS allows you to describe types with interfaces, whereas for Java it is a contract. I.e. nothing needed for the marshalling functionality 🙂

You only need to target the preset against the class renderer as done here.

I would suggest you try to start off with a normal class (an output from Modelina) such as this one:

public class Address {
  private String streetName;
  private String city;
  private String state;
  private Double houseNumber;
  private Boolean marriage;
  private Object members;
  private Object[] arrayType;
  private Map<String, Object> additionalProperties;
  private Map<String, String> sTestPatternProperties;
  public String getStreetName() { return this.streetName; }
  public void setStreetName(String streetName) { this.streetName = streetName; }
  public String getCity() { return this.city; }
  public void setCity(String city) { this.city = city; }
  public String getState() { return this.state; }
  public void setState(String state) { this.state = state; }
  public Double getHouseNumber() { return this.houseNumber; }
  public void setHouseNumber(Double houseNumber) { this.houseNumber = houseNumber; }
  public Boolean getMarriage() { return this.marriage; }
  public void setMarriage(Boolean marriage) { this.marriage = marriage; }
  public Object getMembers() { return this.members; }
  public void setMembers(Object members) { this.members = members; }
  public Object[] getArrayType() { return this.arrayType; }
  public void setArrayType(Object[] arrayType) { this.arrayType = arrayType; }
  public Map<String, Object> getAdditionalProperties() { return this.additionalProperties; }
  public void setAdditionalProperties(Map<String, Object> additionalProperties) { this.additionalProperties = additionalProperties; }
  public Map<String, String> getSTestPatternProperties() { return this.sTestPatternProperties; }
  public void setSTestPatternProperties(Map<String, String> sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; }
}

And then manually start by adding the methods and implementing them. That way it is easier for you to generate them later as you have something to relate it to 🙂

i.e. you start off with adding something similar to this:

   public string toJson(){...}
   public static Address fromJson(){...}

Hope that made sense, otherwise let me know! 🙂

ritik307 commented 2 years ago

Noob ques: But the interface renderer is missing in Java so do I need to make one for Java or not. I am a bit confused since the code is in TS so if I make an interface renderer for Java won't there be a syntax that is mismatched?

As there is a huge difference between interfaces in TypeScript vs Java because TS allows you to describe types with interfaces, whereas for Java it is a contract. I.e. nothing needed for the marshalling functionality 🙂

You only need to target the preset against the class renderer as done here.

I would suggest you try to start off with a normal class (an output from Modelina) such as this one:

public class Address {
  private String streetName;
  private String city;
  private String state;
  private Double houseNumber;
  private Boolean marriage;
  private Object members;
  private Object[] arrayType;
  private Map<String, Object> additionalProperties;
  private Map<String, String> sTestPatternProperties;
  public String getStreetName() { return this.streetName; }
  public void setStreetName(String streetName) { this.streetName = streetName; }
  public String getCity() { return this.city; }
  public void setCity(String city) { this.city = city; }
  public String getState() { return this.state; }
  public void setState(String state) { this.state = state; }
  public Double getHouseNumber() { return this.houseNumber; }
  public void setHouseNumber(Double houseNumber) { this.houseNumber = houseNumber; }
  public Boolean getMarriage() { return this.marriage; }
  public void setMarriage(Boolean marriage) { this.marriage = marriage; }
  public Object getMembers() { return this.members; }
  public void setMembers(Object members) { this.members = members; }
  public Object[] getArrayType() { return this.arrayType; }
  public void setArrayType(Object[] arrayType) { this.arrayType = arrayType; }
  public Map<String, Object> getAdditionalProperties() { return this.additionalProperties; }
  public void setAdditionalProperties(Map<String, Object> additionalProperties) { this.additionalProperties = additionalProperties; }
  public Map<String, String> getSTestPatternProperties() { return this.sTestPatternProperties; }
  public void setSTestPatternProperties(Map<String, String> sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; }
}

And then manually start by adding the methods and implementing them. That way it is easier for you to generate them later as you have something to relate it to 🙂

i.e. you start off with adding something similar to this:

   public string toJson(){...}
   public static Address fromJson(){...}

Hope that made sense, otherwise let me know! 🙂

Got it 👍

jonaslagoni commented 2 years ago

@ritik307 can you provide the implementation for the toJson and fromJson methods? (forget presets for now 🙂)

If you take a look at what you need to do for the toJson method, we need to create a JSON object and stringify it. i.e. to achieve `return '{"streetName": "somevalue", ...}'.

This can be achieved by either using annotations and external libraries, such as we have for Jackson, or we can build it ourselves. Building it ourselves requires either a library to help us (such as JSON-java), or we can manually build it through string manipulation and other variables.

For example, we could end up with something as simple as:

List propList = new ArrayList();
propList.add("\"streetName\": \"somevalue\"");
String propListCombined = String.join(",", propList);
String json = "{" + propListCombined + "}";

For fromJson we need to do the opposite, here we might require JSON-java to easierly iterate an arbitrary JSON object. Play around with it and then provide the full implementation for above example, it is easier to talk about that then mixing it with presets 🙂

Hope that helped @ritik307

asyncapi-bot commented 2 years ago

:tada: This issue has been resolved in version 0.48.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

asyncapi-bot commented 2 years ago

:tada: This issue has been resolved in version 1.0.0-next.23 :tada:

The release is available on:

Your semantic-release bot :package::rocket: