What happens when parent and child classes have duplicate field names with JSON operator?

← Code Quality · Ref: Q853

When a class hierarchy uses 'default operator $$' (JSON serialization), property names must be unique across the entire hierarchy. In fact they must be unique in every hierarchy, with or without $$: a child that redeclares a parent's field is E02010 (DUPLICATE_PROPERTY_FIELD). Because of that rule a generated $$ can never meet a duplicate key, so there is no separate JSON error.

WHY UNIQUE NAMES ARE REQUIRED

JSON keys must be unique within an object. If a parent had field 'label' and a child also had field 'label', the JSON output would be ambiguous - and, more fundamentally, the object would hold two fields where the author meant one. EK9 rejects the redeclaration at compile time.

CORRECT PATTERN

Use unique field names across the hierarchy:

  defines class
    BaseConfig as open
      configName <- "default"
      default operator ?
    ExtendedConfig extends BaseConfig
      priority <- 0
      default operator ?

INCORRECT PATTERN

  defines class
    BaseConfig as open
      label <- "default"
      default operator ?
    ExtendedConfig extends BaseConfig
      label <- "custom"     // E02010: duplicate property
      default operator ?

See Q200 for JSON basics. See Q202 for JSON operator patterns. See Q763 for inheritance depth.

Example

defines module qa.quality.duplicate.json.property

  defines class

    <?-
      Base configuration class with configName field.
      Uses default operator for JSON serialization.
    -?>
    BaseConfig as open
      configName <- "default"

      BaseConfig()
        -> cn as String
        configName :=: cn

      operator <=> as pure
        -> other as BaseConfig
        <- rtn as Integer: configName <=> other.configName

      operator #? as pure
        <- rtn as Integer: #? configName

      default operator

    <?-
      Extended configuration with unique field name 'priority'.
      Does NOT reuse 'configName', which would be E02010.
    -?>
    ExtendedConfig extends BaseConfig
      priority <- 0

      ExtendedConfig()
        ->
          cn as String
          p as Integer
        super(cn)
        priority :=: p

      //Sameness (<=>) is inherited from BaseConfig and sealed; this gives ? $ $$ :=:
      default operator

  defines program

    JsonPropertyDemo()
      stdout <- Stdout()

      config <- ExtendedConfig("prod", 10)
      if config?
        stdout.println("Config created: " + $config)

Common mistakes

E02010 — Parent BaseConfig already has a field named configName. The child cannot redeclare that name, whatever operators are defaulted. Use a unique field name in the child. See ek9 -h E02010 for details.

Incorrect:

configName <- "custom"

      ExtendedConfig()
        ->
          cn as String
          p as Integer
        super(cn)
        configName :=: cn

Correct:

priority <- 0

      ExtendedConfig()
        ->
          cn as String
          p as Integer
        super(cn)
        priority :=: p
Other ways to ask this
  • Is reusing a parent field name a JSON error in EK9?
  • Why does EK9 flag duplicate property names in JSON?
  • Can parent and child have the same field name with operator $$?
  • How does EK9 prevent ambiguous JSON output?

Coming from another language?

Java: Jackson allows field shadowing (confusing JSON). Python: no compile-time JSON validation. Kotlin: Kotlinx serialization detects some conflicts. Rust: Serde handles field shadowing but can be confusing. Go: encoding/json uses last field wins. EK9: E02010 rejects a redeclared property name at compile time, so duplicate JSON keys cannot arise.

Keywords: duplicate, serialization, JSON, field, property, compile-time, E02010, hierarchy, ambiguous, operator