Skip to content

Fix key camel case#2177

Open
iweiu wants to merge 6 commits into
MessagePack-CSharp:masterfrom
iweiu:fix-keyCamelCase
Open

Fix key camel case#2177
iweiu wants to merge 6 commits into
MessagePack-CSharp:masterfrom
iweiu:fix-keyCamelCase

Conversation

@iweiu

@iweiu iweiu commented Mar 25, 2025

Copy link
Copy Markdown

Add KeyName CamelCase

@AArnott AArnott left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing!
I have concerns about this change. My feedback is just my thoughts. @neuecc should chime in too, including around whether he likes the idea of this in general.

Comment thread src/MessagePack.Annotations/Attributes.cs Outdated
Comment thread src/MessagePack.Annotations/Attributes.cs Outdated
Comment thread src/MessagePack.Annotations/Attributes.cs Outdated
/// </summary>
/// <param name="keyBeUpperCamel"></param>
/// <returns></returns>
internal static string ToLowerCamel(string keyBeUpperCamel)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newtonsoft.Json's camelCase conversion function is far more complicated than this. I don't know why. Maybe it transforms _ThisHere to _thisHere as well. As whatever this function does will be locked in for schema compatibility reasons, we should be mindful about the various prior art in this area and be sure we are comfortable with the design.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MessagePackObject]
public class Test {
public string Name { get; set; }
public int Age { get; set; }
public string FullName { get; set; }
}

IF [MessagePackObject(keyAsPropertyName = true,keyNameCamelCase =true)]
The current Keys are: name,age,fullName

ELSE IF [MessagePackObject(keyAsPropertyName = true)]
The current Keys are: Name,Age,FullName

ELSE [MessagePackObject(keyNameCamelCase =true)]
The current Keys are: name,age,fullName

@iweiu iweiu requested a review from AArnott March 26, 2025 04:56
/// <param name="keyNameCamelCase">
/// set keyNameCamelCase=true Convert string type Key to CamelCase rule.
/// </param>
public MessagePackObjectAttribute(bool keyAsPropertyName, bool keyNameCamelCase)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for reverting the change to the existing constructor.
I wonder if we need this constructor though. It will require folks to pass in [MessagePackObject(true, true)] to get to the camel case setting. But if you omit this constructor, this syntax is (still) available which IMO reads better:

[MessagePackObject(KeyNameCamelCase = true)]

@AArnott AArnott Mar 27, 2025

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, if you take my suggestion about enums, a new constructor would work that would be even simpler:

[MessagePackObject(KeyPolicy.CamelCasePropertyNames)]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Not quite what I was thinking of, but I pushed a change to show my intent.

@iweiu iweiu requested a review from AArnott March 28, 2025 02:48

@AArnott AArnott left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is acceptable for dynamic resolver generation at this point. Thanks.
The remaining work is to:

  • ensure the new policy is honored by the source generator path as well.
  • add tests for the new option, for both serialization paths source generation and dynamic object resolver.

@iweiu iweiu requested a review from AArnott April 10, 2025 09:41

@AArnott AArnott left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your latest iteration has some good changes. Thank you.
We still need the source generation side to update to support the new property name transformation.

Comment thread README.md
// {"Foo":10}
Console.WriteLine(MessagePackSerializer.SerializeToJson(new Sample3 { Foo = 10, Bar = 20 }));

// {"foo":10,"bar":20}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make a more informative sample if the property names were multiple words long, so that those not familiar with camel case can readily see that only the first letter is lowercase and the rest are left alone.

/// Gets a value indicating whether to automatically serialize all internal and public fields and properties using their property name as the key in a map.
/// </summary>
[Obsolete($"Use {nameof(KeyPolicy)} instead.")]
public bool KeyAsPropertyName { get; }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, can't do that. Removing a public property is a breaking change. That's why I added the Obsolete attribute instead of removing it.

What we could do though, is replace it with something that doesn't need to be set in the constructor, like this:

[Obsolete($"Use {nameof(KeyPolicy)} instead.")]
public bool KeyAsPropertyName => this.KeyPolicy is KeyPolicy.ImplicitPropertyNames or KeyPolicy.ImplicitCamelCasePropertyNames;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think KeyAsPeopleName can be cleaned up before merging the main code.
Using Obsolvete will make the code more chaotic, and concise code will make it clearer to understand.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You seem to think this is up for debate. It is not. Breaking changes are unacceptable in a stable library such as this one. The property must remain. The only question is whether to add the [Obsolete] attribute to it. In this case I believe it should, because it is both redundant with the new property and somewhat hazardous for anyone depending on it since it no longer is guaranteed to mean what it used to as a 3rd mode has been added.
And considering almost no one is ever reading this property outside of this library, adding Obsolete to it is unlikely to upset anyone.

public const int Prop1Constant = 102;
public const int Prop2Constant = 2223;

public int Prop1 { get; set; } = Prop1Constant;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tests. They'll be more useful if the property names had multiple uppercase letters in them so that we can verify that only the first letter is changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants