Migration from v3 to v4

This document contain all the breaking changes and migrations guidelines for adapting your code to the new version.

Deprecation of processor.interpreter

Since the early days we had the option to set processorOptions.interpreter options to change how JSON Schema is interpreted to Meta models. However, these options are more accurately part of the processorOptions.jsonSchema options.

Use this instead going forward.

Fixed edge cases for camel case names

Naming such as object properties using camel case formatting had an edge case where if they contained a number followed by an underscore and a letter it would be incorrectly formatted. This has been fixed in this version, which might mean properties, model names, etc that use camel case might be renamed.

This example contains such a string:

1type: object
2properties:
3  aa_00_testAttribute:
4    type: string

This used to generate:

1interface AnonymousSchema_1 {
2  aa_00TestAttribute?: string;
3}

but will now generate:

1interface AnonymousSchema_1 {
2  aa_00_testAttribute?: string;
3}

C#

Constant values are now properly rendered as const properties

This example used to generate a string with a getter and setter, but will now generate a const string that is initialized to the const value provided.

1type: object
2properties:
3  property:
4    type: string
5    const: 'abc'

will generate

1public class TestClass {
2  private const string property = "test";
3
4  public string Property
5  {
6    get { return property; }
7  }
8  ...
9}

Notice that Property no longer has a set method. This might break existing models.

DateTime and DateTimeOffset are now properly rendered based on specification format

In the previous version, date-time and date formats were rendered as DateTime and DateTimeOffset respectively. This has been changed to render DateTimeOffset for date-time and DateTime for date formats.

This might break existing implementation and require manual changes.

The best thing to do is to fix your specification and use what you really need. If you don't care about the time and time zone, use date instead of date-time. Otherwise, keep the date-time format and update your code to use DateTimeOffset instead of DateTime. That usually means doing this:

1var dateTime = new DateTime(2008, 6, 19, 7, 0, 0);
2
3// Set the DateTime property of the ModelinaModel
4var modelinaModel = new ModelinaModel();
5modelinaModel.DateTime = dateTime;
6Console.WriteLine(modelinaModel.DateTime);
7
8// Get the DateTime property from the ModelinaModel
9DateTime dateTime2 = modelinaModel.DateTime.LocalDateTime;
10Console.WriteLine(dateTime2);

Python

Models are aiming to be >= v3.7 compliant.

Unique types in unions

In v4, unions types are rendered unique, meaning you will never see str | str but just str.

Pydantic now follows v2 instead of v1

Reference: https://docs.pydantic.dev/2.6/migration/

The schema description is now a description and not an alias:

1class Message(BaseModel):
2  identifier: str = Field(description='''The Identifier for the Message''')

In Modelina v3 this used is rendered as:

1class Message(BaseModel):
2  identifier: str = Field(alias='''The Identifier for the Message''')

Following standardized styling guide

Before names of properties and model names did not follow any specific styling standard.

In v4, we switched to using the following:

This means that properties and their accessor methods (getter and setters) have been renamed from:

1- self._someWeirdValueExclamationQuotationHash_2 = input.someWeirdValueExclamationQuotationHash_2
2+ self._some_weird_value_exclamation_quotation_hash_2 = input.some_weird_value_exclamation_quotation_hash_2

And model names have been renamed to:

1- class AsyncApi_3Dot_0Dot_0SchemaDot:
2+ class AsyncApi3Dot0Dot0SchemaDot:

If you are using the Python preset PYTHON_JSON_SERIALIZER_PRESET, the functions have also been renamed:

1- serializeToJson
2+ serialize_to_json
3
4- deserializeFromJson
5+ deserialize_from_json

Type hints

Classes now have type hints on all properties and accessor functions.

Before:

1from typing import Any, Dict
2class ObjProperty:
3  def __init__(self, input):
4    if hasattr(input, 'number'):
5      self._number = input['number']
6    if hasattr(input, 'additional_properties'):
7      self._additional_properties = input['additional_properties']
8
9  @property
10  def number(self):
11    return self._number
12  @number.setter
13  def number(self, number):
14    self._number = number
15
16  @property
17  def additional_properties(self):
18    return self._additional_properties
19  @additional_properties.setter
20  def additional_properties(self, additional_properties):
21    self._additional_properties = additional_properties

After:

1from typing import Any, Dict
2class ObjProperty:
3  def __init__(self, input: Dict):
4    if hasattr(input, 'number'):
5      self._number: float = input['number']
6    if hasattr(input, 'additional_properties'):
7      self._additional_properties: dict[str, Any] = input['additional_properties']
8
9  @property
10  def number(self) -> float:
11    return self._number
12  @number.setter
13  def number(self, number: float):
14    self._number = number
15
16  @property
17  def additional_properties(self) -> dict[str, Any]:
18    return self._additional_properties
19  @additional_properties.setter
20  def additional_properties(self, additional_properties: dict[str, Any]):
21    self._additional_properties = additional_properties

Initialization of correct models

Before in the constructor, if a property was another class, they would not correctly be initialized. Now a new instance of the object is created.

Constants are now rendered

Before, constants where completely ignored, now they are respected and also means you don't have the possibility to change it through setters for example.

1class Address:
2  def __init__(self, input: Dict):
3    self._street_name: str = 'someAddress'
4
5  @property
6  def street_name(self) -> str:
7    return self._street_name

Import style deprecation

All models are from this point onwards imported using explicit styles from . import ${model.name} to allow for circular model dependencies to work. This means that the option importsStyle is deprecated and is no longer in use. It will be removed at some point in the future.

Go

File names

In v4, file names for go will be formatted as snake_case.go. This is the "standard" in go: https://github.com/golang/go/issues/36060

Union types will be generated correctly with struct embeddings

Modelina now supports union types for Go. Since Go does not have native union types support modelina uses struct embeddings to mock union types.

1type Union struct {
2  CustomStruct
3  int
4  string
5  ModelinaArrType []string
6  ModelinaDictType map[string] interface{}
7  ModelinaAnyType interface {}
8}

ModelinaArrType, ModelinaDictType, ModelinaAnyType are generated by default by modelina, users can change the names by passing different names to the GoGenerator Options, for example:

1const generator = new GoGenerator({
2  unionAnyModelName: 'ModelinaAnyType',
3  unionArrModelName: 'ModelinaArrType',
4  unionDictModelName: 'ModelinaDictType'
5});

Union type with discriminator will render an interface and the children will implement that interface

While the above changes work for primitives, it's problematic for objects with a discriminator. This is solved by creating an interface for the parent with the discriminator and each child implements the interface:

1type Vehicle interface {
2  IsVehicleVehicleType()
3}
4
5type Car struct {
6  VehicleType *VehicleType
7  RegistrationPlate string
8  AdditionalProperties map[string]interface{}
9}
10
11func (r Car) IsVehicleVehicleType() {}
12
13type Truck struct {
14  VehicleType *VehicleType
15  RegistrationPlate string
16  AdditionalProperties map[string]interface{}
17}
18
19func (r Truck) IsVehicleVehicleType() {}
20
21type VehicleType uint
22
23const (
24  VehicleTypeCar VehicleType = iota
25  VehicleTypeTruck
26)
27
28// Value returns the value of the enum.
29func (op VehicleType) Value() any {
30	if op >= VehicleType(len(VehicleTypeValues)) {
31		return nil
32	}
33	return VehicleTypeValues[op]
34}
35
36var VehicleTypeValues = []any{\\"Car\\",\\"Truck\\"}
37var ValuesToVehicleType = map[any]VehicleType{
38  VehicleTypeValues[VehicleTypeCar]: VehicleTypeCar,
39  VehicleTypeValues[VehicleTypeTruck]: VehicleTypeTruck,
40}

Nullable and required properties

Modelina now has support for nullable and required properties in go structs. This support exists for generic types like int, string, bool, float64.

1type info struct {
2  name string // required
3  description *string // nullable
4  version *float64
5  isDevelopment *bool
6}

Java

when allowInheritance is true, Modelina now don't render the setter for enums in interfaces because the classes that implement the interface might use a constant value

In Java, when a class implements an interface, it must implement all the methods of that interface. Because of that, Modelina now doesn't render the setter for enums in interfaces when allowInheritance is true because the classes that implement the interface might use a constant value.