JohnWeisz / TypedJSON

Typed JSON parsing and serializing for TypeScript that preserves type information.
MIT License
603 stars 64 forks source link

Add Getter/Setter in IJsonMemberOptions #148

Closed DesselBane closed 4 years ago

DesselBane commented 4 years ago

Hi, I'm working with Vue 3 and the @vue/reactivity package. My data objects often look like this:

export class Trait {
  public name: Ref<string> = ref('')
  public value: Ref<string> = ref('')
}

where the Ref<T> type is a vue specific thing to tell the UI when something changed. Right now, if I want to serialize this, I would have to write code somehting like this:

@jsonObject
export class Trait {
  @jsonMember({ constructor: String, name: 'name' })
  private get json_name(): string {
    return this.name.value
  }

  private set json_name(newValue: string) {
    this.name.value = newValue
  }

  @jsonMember({ constructor: String, name: 'value' })
  private get json_value(): string {
    return this.value.value
  }

  private set json_value(newValue: string) {
    this.value.value = newValue
  }

  public name: Ref<string> = ref('')
  public value: Ref<string> = ref('')
}

The Ref<T> type is no class and also out of my reach since it comes from a framework. I also tried using an options object with a deserializer/serializer combo. Problem here is that a Ref<T> may contain any object not just primitives (as shown in this example).

A possible solution might look something like this:

import { myCustomRefOptions } from '@/helpers'
export class Trait {
  @jsonMember({ constructor: String, getter: (member) => member.value, setter(member, newValue ) => { member.value = newValue  })
  public name: Ref<string> = ref('')

  @jsonMember(myCustomRefOptions)
  public value: Ref<string> = ref('')
}

The difference between this and the de/serializer properties is that the return value of the getter still needs to be serialized and the newValue in the setter already was parsed.

Or maybe this is already possible and I just dont know how to do it?

Neos3452 commented 4 years ago

Hi @DesselBane, I am not sure if I understood your problem correctly. But if you just want to serialise the inner value, you can always call TypedJSON from within the serialiser/deserialiser like below. Let me know if this helps you.

    @jsonObject
    class Person {
        @jsonMember
        firstName: string;

        @jsonMember
        lastName: string;

        getFullName() {
            return `${this.firstName} ${this.lastName}`;
        }
    }

    interface Ref<T> {
        value: T;
    }

    function serializeRef<T>(type: Serializable<T>, ref: Ref<T>): any {
        return TypedJSON.toPlainJson(ref.value, type);
    }

    function deserializeRef<T>(type: Serializable<T>, value: any): Ref<T> {
        return { value: TypedJSON.parse(value, type) }
    }

    @jsonObject
    class ContainsRefs {
        @jsonMember({serializer: ref => serializeRef(String, ref), deserializer: json => deserializeRef(String, json)})
        refStr: Ref<string>;

        @jsonMember({serializer: ref => serializeRef(Person, ref), deserializer: json => deserializeRef(Person, json)})
        refObj: Ref<Person>;
    }
DesselBane commented 4 years ago

Hi thanks for the reply. It seems to be working 👍 🥇