question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Non constructor parameters properties are ignored

See original GitHub issue

Hi, I’m migrating an app from moshi-kotlin to kotshi, until I got an unexpected KotlinNPE caused by JSON parsing error.

The expected property was a var in my @JsonSerializable data class, that is intentionally not part of the constructor, because I don’t want its value to be part of the data class stuff like equals, hashCode, copy…

Here’s a digest example snippet:

@JsonSerializable
data class MyUser(
        val id: String,
        @Json(name = "firstname")
        val firstName: String,
        @Json(name = "lastname")
        val lastName: String?
) {
        @Json(name = "support_tickets")
        @Ignore // Used for Android Arch Room
        var supportTickets: List<SupportTicket>? = null
}

In the example above, if there’s a list with the support_tickets expected key in the input JSON, it’ll be ignored andsupportTickets will still be null, because version 0.3.0-beta1 unfortunately doesn’t generate any code for properties not in the default constructor.

When I was using moshi-kotlin with the gigantic kotlin-reflect library, there was no bug in this case, it went as expected with no KNPE for when the value was expected.

Hope my bug report is clear and you can find a path to a proper fix! 😃

Have a nice day!

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:15 (15 by maintainers)

github_iconTop GitHub Comments

1reaction
LouisCADcommented, Dec 18, 2017

So, to clarify: my use case is to ignore property with Room, while still getting it from the JSON.

Your second constructor trick worked perfectly, and while I had to add a bit more boilerplate for the constructor delegation considering the high number of parameters, it enabled me to separate the Room annotations (@Embedded) that are seen in the main constructor from the Kotshi/Moshi ones (@Json(name= "…")) that are seen in the secondary one. It makes it clearer what is persisted in DB and what is got from the remote API.

Thanks for your help, again!

I’m closing this issue now that this limitation is documented.

1reaction
ansmancommented, Dec 18, 2017

@LouisCAD I would prefer to not have support for that unless there is a very compelling argument. I don’t think we should consider properties declared outside the primary constructor to be a part of that class. This is similar to how Kotlin treats data classes (it doesn’t include those properties in hashCode, equals or toString) which to me means that Kotlin’s intentions are for those properties to be considered computed or cached.

One “hack” you could potentially use is to have two constructors like this:

@JsonSerializable
data class Foo(val prop1: String) {
    var prop2: String? = null

    @KotshiConstructor
    constructor(prop1: String, prop2: String?) : this(prop1) {
        this.prop2 = prop2
    }
}

Generated adapter:

public final class KotshiFooJsonAdapter extends JsonAdapter<Foo> {
  private static final JsonReader.Options OPTIONS = JsonReader.Options.of(
      "prop1",
      "prop2");

  public KotshiFooJsonAdapter() {
  }

  @Override
  public void toJson(JsonWriter writer, Foo value) throws IOException {
    if (value == null) {
      writer.nullValue();
      return;
    }
    writer.beginObject();
    writer.name("prop1");
    writer.value(value.getProp1());
    writer.name("prop2");
    writer.value(value.getProp2());
    writer.endObject();
  }

  @Override
  public Foo fromJson(JsonReader reader) throws IOException {
    if (reader.peek() == JsonReader.Token.NULL) {
      return reader.nextNull();
    }
    reader.beginObject();
    String prop1 = null;
    String prop2 = null;
    while (reader.hasNext()) {
      switch (reader.selectName(OPTIONS)) {
        case 0: {
          if (reader.peek() == JsonReader.Token.NULL) {
            reader.nextNull();
          } else {
            prop1 = reader.nextString();
          }
          continue;
        }
        case 1: {
          if (reader.peek() == JsonReader.Token.NULL) {
            reader.nextNull();
          } else {
            prop2 = reader.nextString();
          }
          continue;
        }
        case -1: {
          reader.nextName();
          reader.skipValue();
          continue;
        }
      }
    }
    reader.endObject();
    StringBuilder stringBuilder = null;
    if (prop1 == null) {
      stringBuilder = KotshiUtils.appendNullableError(stringBuilder, "prop1");
    }
    if (stringBuilder != null) {
      throw new NullPointerException(stringBuilder.toString());
    }
    return new Foo(
        prop1,
        prop2);
  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - How to auto-ignore all properties with no corresponding ...
Is there some non-attribute-based method of ignoring all properties that don't have a corresponding constructor parameter when serializing?
Read more >
parameter-properties | typescript-eslint
Require or disallow parameter properties in class constructors. ... "allow" : allowing certain kinds of properties to be ignored; "prefer" : either ...
Read more >
Entity types with constructors - EF Core - Microsoft Learn
Not all properties need to have constructor parameters. ... This can be ignored since in reality EF Core is using the field in...
Read more >
constructor - JavaScript - MDN Web Docs - Mozilla
The constructor method is a special method of a class for creating and initializing an object instance of that class.
Read more >
BeanUtils (Spring Framework 6.0.3 API)
Note that this method tries to set the constructor accessible if given a non-accessible (that is, non-public) constructor. Parameters: clazz - the class...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found