class-transformer
class-transformer copied to clipboard
feat: JSON property order
@PropertyOrder([ 'id', 'password', 'email' ])
export class User {
email: string;
id: number;
password: string;
}
plainToClass(User, jsonData) // User { id: 1, password: 'xxx', email: '[email protected]' }
Or just preserving the order they are declared in. This is a real pain for me.
Bump! .... Can we please look into this? I think this will be a great addition.
Can you guys explain the reasoning behind this? Why is it important to be able to specify the property order on the object?
@NoNameProvided The real reasoning I feel is readability in responses.
When using Class Transformers, the response changes into the order of
- Non-transformed properties
- Transformed properties
If we have another decorator defining the order or maybe just preserve the order the properties are declared in, it'll ensure higher response readability.
@NoNameProvided Bump again.
This is low prio for me, I still don't see the justification for this. If we can agree on some acceptable design someone can try to implement it, but first we need to agree on the API.
+1 to caring about property order. And I have a concrete example:
I have server-side C# classes, and I'm using JSON.NET to move them back and forth over the wire.
Now, that library expects a $type
property as the first property of a JSON object to be able to tell which class to deserialize into, particularly when it can't infer it from context. If I have to guess, it's because it's reading the JSON as a stream, and it needs to know which class to instantiate before taking in the rest of the object.
This is very similar to and ties neatly with class-transformer's discriminator. But currently CT's classToPlain
is putting it at the end of the object, which I have to work around.
It's not exactly what this issue is about, but it's pretty close.
+1 to caring about property order. And I have a concrete example:
I dump data into JSON files which are under git control. Since I started to use plainToInstance, the JSON files are modified only because of properties order. I would like the order to stay the same for speeding up diffing of modifications.
@boutchitos Everyone has a different use-case tbh.
Typestack team - Will be great if we can get something done on this. I have to create customer transformer classes to fix this issue (see below). This is not ideal.
export class UserTransformer implements User {
id!: string;
firstName!: string;
lastName: string;
name: string;
email!: string;
@Expose({ groups: ['user.timestamps'] })
createdAt: Date;
@Expose({ groups: ['user.timestamps'] })
updatedAt: Date;
constructor(partial?: Partial<UserTransformer>) {
const variablesToAssign = [
'id',
'firstName',
'lastName',
'name',
'email',
'createdAt',
'updatedAt',
];
variablesToAssign.forEach((variable) => {
if (!partial[variable]) {
partial[variable] = null;
}
Object.assign(this, { [variable]: partial[variable] });
});
}
}
I'd really appreciate if this is prioritized.
CC: @NoNameProvided
+1 and I have another example: Private/public key signing. I need to sign my JSON payload with private key and verify the signature with public key. Thus I need to have a way to serialize my payload in a deterministic way. The order of fields should always be the same (in my case they should be sorted alphabetically).
If it helps anyone: I found if I set excludeExtraneousValues = true in ClassTransformOptions and explicitly @ Expose everything, properties are output in the "correct" order. For my case at least, I think the disorder comes when the array of object keys is concatenated with the array of "exposed properties" in getKeys() in TransformOperationExecutor.js. So with excludeExtraneousValues = true, the object keys are skipped and just the @ Exposes are read in order. That said, I can see value in an @ Order property/method/accessor decorator that takes a single number (defaulting to 0) and is read in transform() or getKeys().
I'm also experiencing issues with the order. Especially when using the @Type
decorator.
When doing the plainToInstance
transformation, the property with the @Type
decorator is checked before the the discriminator property has been checked, therefore it couldn't find the right type to convert to:
Happens in this area of code: https://github.com/typestack/class-transformer/blob/develop/src/TransformOperationExecutor.ts#L232
One way i found to bypass it is with the following code:
@IsEnum(DtoType)
type: DtoType;
@Type(obj=> {
switch (obj.object.type) {
case DtoType.A:
return TypeADto;
case DtoType.B:
return TypeBDto;
}
})
attributes: TypeADto | TypeBDto
i care about property order for a couple of reasons:
-
readability: in nestjs projects, i use a
PaginatedDTO
class. if i have an api endpoint that sends paginated responses, i want an array value listed after a primitive value (e.g.{ "count": 0, "limit": 20, "offset": 0, "data": [] }
). similarly, if i have an endpoint that sendsUser
data,id
should be the first field shown when sending back a response - preference: in most cases, i prefer fields to be listed alphabetically. in my case, this is also for readability 😊
Something I've experienced with working with legacy providers over SOAP apis - for them the XML elements order matter, which is making working with them in TypeScript get tedious.
Working with dderevjanik/wsdl-tsclient we get interfaces, and we have to make sure that the JSON that we send have the order of propertoes match the order of properties in the interface.
Ideally - we could change the interfaces to classes and use plainToClass()
to get the properties in the correct order as set in the class.
I just ran into this issue as well.
One use case is if you are signing the JSON response with an cryptographic key for the purposes of e.g. non-repudiation, the order of the fields must be identical from what is signed versus ultimately sent to a client. In popular frameworks like Nest.js, it is common to use class-transformer
on the boundary of an API for validation purposes. This then introduces the issue where a known field order needs to be maintained but class-transformer
provides no way to influence the order.
Workarounds exist but perhaps if it were possible to simply pass our own sort function as a serializer option, then it could be trivially controlled with minimal API changes.