denostack / superserial

A comprehensive Serializer/Deserializer that can handle any data type.
MIT License
35 stars 3 forks source link

import Class from other file doesn't work #10

Open AresJ1 opened 6 months ago

AresJ1 commented 6 months ago

Class TestUser is not defined. It will be ignored.

export class TestUser { public constructor( public name?: string, public age?: number, ) { } }

import { TestUser } from "../stage/StageData";

    const serializer = new Serializer();
    const serialized = serializer.serialize(new TestUser("wan2land", 20));

    console.log($"Serialize: {serialized}");
    const user = serializer.deserialize(serialized);

     console.log(`Serialize check ${user}`);

      Class TestUser is not defined. It will be ignored.

      env:cocos creator 3.8.2  ES2015
wan2land commented 6 months ago

This is because when you deserialise the string TestUser{"name":"wan2land","age":20}, the serialiser doesn't know what class to map TestUser to.

There are two ways to do this.

1.

const serializer = new Serializer({
  classes: {
    TestUser,
  },
});

2.

const serializer = new Serializer({
  loadClass(name: string) {
    if (name === "TestUser") {
      return TestUser;
    }
    return null;
  }
});
AresJ1 commented 6 months ago

It was incredibly helpful. export const serializer = new Serializer({ classes: { "StageData": StageData, "StageSetting": StageSetting, "StageInfo": StageInfo, "ShopData": ShopData, "GroupDeck": GroupDeck, "GroupCard": GroupCard, "GroupHand": GroupHand, "CardDeck": CardDeck, "RoundNetWorkHand": RoundNetWorkHand, "CardPool": CardPool, "Serializer": Serializer, "StageRule": StageRule, "SettlementData": SettlementData, "AllRankInfo": AllRankInfo, "PokerRankType": PokerRankType, "VoucherCardRateConfig": VoucherCardRateConfig, "BuffContainer": BuffContainer, "RoundData": RoundData, "CardPoker": CardPoker, "ObjectPool": ObjectPool, "CardBlind": CardBlind, "CardTag": CardTag, "OneRankInfo": OneRankInfo, "BlindDesc": BlindDesc, "Buff": Buff, "EffectPlayPoker": EffectPlayPoker, "EffectTagEffect": EffectTagEffect, "EffectOperateCountAddMoney": EffectOperateCountAddMoney, "EffectWeakenPoker": EffectWeakenPoker, "EffectClearWeaken": EffectClearWeaken, "TargetCurrent": TargetCurrent, "ConditionPlayedThisStage": ConditionPlayedThisStage } });

AresJ1 commented 6 months ago

I encountered a TypeError during data deserialization. The specific error message is as follows:

TypeError: Cannot read property 'name' of undefined
AresJ1 commented 6 months ago

I encountered a TypeError during data deserialization. The specific error message is as follows:

TypeError: Cannot read property 'name' of undefined

Solved! The issue was due to the class not having a default constructor defined, which caused problems during the deserialization process.

      case 17: {
        const name = ast[1];
        const entries = ast[2];

        const baseClass = name ? loadClass(name) ?? null : null;
        // Suggestion: Consider handling cases where baseClass lacks a default constructor.
        const value = baseClass ? Reflect.construct(baseClass, []) : {};
wan2land commented 6 months ago

Could you please reduce the testcase to the minimum number of objects that throw an error and provide a serialised string? 🙏

wan2land commented 6 months ago

Could you please attach the stack trace of the error? The error message indicates that 'name' is accessed as undefined, but there appears to be no relevant code in 'deserialize'.

let foo = undefined

something[name]; // (X)
something.name; // (O)
something['name'] // (O)

Could you clarify what is meant by a 'default constructor'? I was under the impression that it refers to the absence of any constructor, so I added a test case accordingly, but I didn't encounter the expected error.

https://github.com/denostack/superserial/commit/04770b90351123ecaea969ad42893dc70071d5a9

AresJ1 commented 6 months ago

Could you please attach the stack trace of the error? The error message indicates that 'name' is accessed as undefined, but there appears to be no relevant code in 'deserialize'.

let foo = undefined

something[name]; // (X)
something.name; // (O)
something['name'] // (O)

Could you clarify what is meant by a 'default constructor'? I was under the impression that it refers to the absence of any constructor, so I added a test case accordingly, but I didn't encounter the expected error.

04770b9

By changing the TestUser class to the one below, it no longer has a default constructor.

class TestUser {
    public constructor(userName: string) {
        this.userName = userName
    }

    public userName?: string;
    public age?: number;
}
AresJ1 commented 6 months ago

maybe best practice:

import { Serializer } from "superserial";

export const serializer = new Serializer({
    classes: {
    }
});

interface ClassConstructor<T> {
    new(): T;
    name: string;
}

export function registerClassToSerializer<T>(target: ClassConstructor<T>): void {
    const className = target.name;

    if (!serializer.options) {
        serializer.options = {};
    }

    if (!serializer.options.classes) {
        serializer.options.classes = {};
    }

    if (!serializer.options.classes[className]) {
        serializer.options.classes[className] = target;
        console.log("registerClassToSerializer:", className);
    }
}