Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow a custom Json serialization option when creating a jwt using Newtonsoft.Json #119

Closed
PureKrome opened this issue Jun 22, 2017 · 10 comments
Assignees
Labels

Comments

@PureKrome
Copy link
Contributor

Hi 👋

Would you folks accept a PR for allowed an optional JsonSerializer to be added to the JsonNetSerializer class?

So I've got some custom Poco (lets say, a User or an Account). In it, is an enum of .. say .. colours. Right now, the values in the JWT are numbers. Yes, it's fine and all ... I'm just OCD. So right now I've enabled the option to pass in a JsonSerializer to my CustomJwtJsonSerializer (a copy of that linked file, above) which now prints the string version of my claims, out.

I totally get it's just eye candy .. but would you guys be happy to at least review a PR to see if that might be handy to another OCD asshat like me?

(I use jwt.io to decode the jwt to see what contents are in there -> and numbers don't help me see what enums values are, etc).

Thanks kindly 😄

@PureKrome PureKrome changed the title Allow most custom Json serialization options when creating a jwt using Newtonsoft.Json Allow a custom Json serialization option when creating a jwt using Newtonsoft.Json Jun 22, 2017
@abatishchev
Copy link
Member

Hi!
So you're wanting to have an overload added? Or a class?
Can you provide an example?

@nbarbettini
Copy link
Contributor

I'm assuming @PureKrome is talking about an overload. By passing a custom JsonSerializer the user could customize how things are serialized (by providing custom JsonConverters, and so on).

@abatishchev
Copy link
Member

I don't mind adding an overload of course (spite adding one to the interface would be a breaking change but who minds?) but not sure how it would look like? Also to have a custom JsonSerializer one just needs to implement the interface differently rather than adding more to the existing implementation. Right?

@abatishchev
Copy link
Member

Ok, I see now what you're saying. To the addition of existing:

        public string Serialize(object obj)
        {
            return JObject.FromObject(obj).ToString(Formatting.None);
        }

Add one or more:

        public string Serialize(object obj, JsonConverter[] converters)
        {
            return JObject.FromObject(obj).ToString(Formatting.None, converters);
        }

        public string Serialize(object obj, JsonSerializer serializer)
        {
            return JObject.FromObject(obj, serializer).ToString(Formatting.None);
        }

        public string Serialize(object obj, JsonSerializer serializer, JsonConverter[] converters)
        {
            return JObject.FromObject(obj, serializer).ToString(Formatting.None, converters);
        }

We need default interface implementation from C# 8.0 here!

@PureKrome
Copy link
Contributor Author

PureKrome commented Jun 22, 2017

actually nope - that wasn't what i was thinking. I do not want to change the interface.

This is very specific to the Newtonsoft.Json implementation of a Jwt.IJsonSerilizer.

this was what i have right now...

namespace Hornet.Api.Services.Authentication
{
    public class HornetJwtJsonSerializer : IJsonSerializer
    {
        private static readonly JsonSerializer JsonSerializer = new CustomJsonSerializer(); // My JS with custom settings, like identing, camelcasing, enums->to->string converter, etc.

        /// <summary>
        /// Serialize the given object.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <returns></returns>
        public string Serialize(object obj)
        {
            return JObject.FromObject(obj, JsonSerializer).ToString(Formatting.None);
        }

        /// <summary>
        /// Deserialize the given string.
        /// </summary>
        /// <typeparam name="T">The type to deserialize the string to.</typeparam>
        /// <param name="json">The JSON to be deserialized.</param>
        /// <returns></returns>
        public T Deserialize<T>(string json)
        {
            return JObject.Parse(json).ToObject<T>();
        }
    }
}

but it's hardcoded for my own use.

I was going to suggest modifying this code to include a constructor which takes an optional Newtonsoft.JsonSerializer or maybe making the property a public static. The idea is that it only needs to be instansiated once and not once-each-time-it's-called.

I guess my assumption is this -> the settings are global and they wouldn't change, on demand.

@abatishchev
Copy link
Member

abatishchev commented Jun 23, 2017

I frankly wouldn't like to add static members as we already had this in 2.x and abandoned it in 3.x. Basically the main change between version was to introduce interfaces to simplify customization, such as this case. So let's find out another way.

Do you know how these overloads are implemented in Json.Net? Does the shortest pass null to the longest?

I'm asking because we can do something like this:

// unseal
public class JsonNetSerializer : IJsonSerializer
{
  // virtual
  public virtual string Serialize(object obj)
  {
    return Serialize(obj, null);
  }

  protected static string Serialize(object obj, JsonSerializer serializer)
  {
    // null doesn't work here, stupid
    serializer = serializer ?? JsonSerializer.CreateDefault();
    return JObject.FromObject(obj, serializer).ToString(Formatting.None);
  }
}

So you'll have:

public class HornetJwtJsonSerializer : JsonNetSerializer 
{
  private static readonly JsonSerializer _jsonSerializer = new CustomJsonSerializer();

  public override string Serialize(object obj, JsonSerializer serializer)
  {
    return Serialize(obj, jsonSerializer);
  }
}

@abatishchev
Copy link
Member

abatishchev commented Jun 23, 2017

Or indeed we can add a ctor accepting JsonSerializer alongside with default ctor:

public sealed class JsonNetSerializer : IJsonSerializer
{
    public JsonNetSerializer()
      : this(JsonSerializer.CreateDefault())
    {
    }

    public JsonNetSerializer(JsonSerializer serializer)
    {
        _serializer = serializer;
    }

    public string Serialize(object obj)
    {
      return JObject.FromObject(obj, _serializer).ToString(Formatting.None);
    }
}

@PureKrome
Copy link
Contributor Author

I frankly wouldn't like to add static members

Kewl - Agree. messes up testing. That's why I suggested the ctor option.

protected static string Serialize(object obj, JsonSerializer serializer)

The reason I personally suggested against this is because the INTERFACE now has a dependency on Newtonsoft.Json (which i think ruins having an agnostic interface). Correct?

Or indeed we can add a ctor accepting JsonSerializer alongside with default ctor:

That's been my preference all along. I prefer this because it doesn't tie it to the interface and is now specific to this Newtonsoft.Json implementation.

I'm happy to PR it .. even though u've basically done the exact code, above 😄

@abatishchev
Copy link
Member

Resolved by #120

@PureKrome
Copy link
Contributor Author

thank you!

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

No branches or pull requests

3 participants