1

Let us consider a class

public class Money
{
    public decimal Value { get; set; }
}

Let us say this is part of another class,

public class UserAccount
{
    public Money Balance { get; set; }
}

On serialization UserAccount produces something like,

{
   "balance": {
       "value":  1.0
   }
}

Now I want to convert it to/from a json like this.

{
 "balance": 1.0
}

This is in a way similar to how DateTime is serialized by using Custom formatter. Json.Net and MSDN recommends custom formatter and it works fine. But is there a way to Handle this directly from the Money or UserAccount class?

Edit: Clarification Datetime does this quite often. For example a serialization of Datetime should look like,

{
  "Hour":"00"
  "Minutes" : "00"
   ......
}

But no, it just looks like 04-12-2021 00:00:00

Edit 2: Using a custom formatter like suggested in this MSDN Article works and I am looking to use it as last option. But this implies that every consumer of the library has to implement and looking a way to avoid it.

Code Name Jack
  • 2,856
  • 24
  • 40
  • I am not sure I completely follow: Class userData has a property "userId" And Class test contains a property of an instance of Userdata. So data is technically a new "{}" set of values, the standard for test would be: { "data":{ "UserId":"1" } } not what you have in your example. I would suggest you simply create a new class: "TestJson" public class TestJson() { public string data {get;set;} } and then map the UserId from Userdata into this value to get the result you want. – Morten Bork Apr 19 '21 at 17:22
  • Perhaps this will be helpful [How to serialize and deserialize (marshal and unmarshal) JSON in .NET](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0) – Nicholas Hunter Apr 19 '21 at 17:23
  • What happens when `UserData` contains more than one property? – gunr2171 Apr 19 '21 at 17:25
  • That's the point. The example essentially means that from that one value it should deserialize into the complete class. Value "1" should deserialize to "Data.UserId=1". @MortenBork – Code Name Jack Apr 19 '21 at 17:32
  • @gunr2171 Right now my requirement is with just one field, and but maybe we can have it for multiple fields too. (Datetime again being nice example.) – Code Name Jack Apr 19 '21 at 17:32
  • @NicholasHunter What I am trying to achieve is to unfold a class from single value. – Code Name Jack Apr 19 '21 at 17:34
  • I guess you would have to make a custom Json formatter, but what's the advantage of storing a class as a single string in the first place? At that point, just use Json as intended. – gunr2171 Apr 19 '21 at 17:38
  • So a lot values for such fields are coming from existing systems. And so far we are adding attributes with custom formatter. But the values might be `Money` for example and we don't want to represent it like a plain double. Instead we want to use a money class which has double inside it. I will improve my example with Money class to get some more sense out of it. – Code Name Jack Apr 19 '21 at 17:44
  • To simplify this question, show the class. You could say "When I serialize it, it looks like this." Then show that JSON. Then say, "I want it to look like this instead." Then show how you want it to look. Keep it simple. Including an example of how you'd like `DateTime` to serialize just adds confusion. It's hard to read all of this and figure out exactly which part shows the result you're aiming for. – Scott Hannen Apr 19 '21 at 17:54
  • You have tagged your question [tag:json.net]. If you are really using Json.NET not System.Text.Json, you can use a `TypeConverter` as shown in [Json.Net: Serialize/Deserialize property as a value, not as an object](https://stackoverflow.com/a/40480742/3744182). In fact this may be a duplicate, agree? – dbc Apr 19 '21 at 17:58
  • This answer works great. I will try to use this to experiment with in System.Text.Json. If it works then great. Thanks for digging up. I was not able to find this/ @dbc – Code Name Jack Apr 19 '21 at 18:01
  • @ScottHannen Yes this makes sense let me do that. – Code Name Jack Apr 19 '21 at 18:01
  • I believe (but have not checked for myself) that System.Text.Json does not support type converters out of the box, unfortunately. See [System.Text.Json does not support TypeConverters #38812](https://github.com/dotnet/runtime/issues/38812). – dbc Apr 19 '21 at 18:06
  • Friendly remember to prefer `decimal` over `double` for currency types in C#. – Wyck Apr 19 '21 at 18:08
  • Oh yes, double currency another antipattern. Reason being decimal is base 10 like humans do. – Code Name Jack Apr 19 '21 at 18:10
  • @dbc It has a workaround posted in the end of the github issue.Additioanly I will remove the json.net tag. – Code Name Jack Apr 19 '21 at 18:11

1 Answers1

1

The converter could be as simple as:

public class MoneyJsonConverter : JsonConverter<Money>
{
    public override Money Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options) =>
            new Money { Value = decimal.Parse(reader.GetString(), CultureInfo.InvariantCulture) };

    public override void Write(
        Utf8JsonWriter writer,
        Money v,
        JsonSerializerOptions options) =>
            writer.WriteStringValue(v.Value.ToString(CultureInfo.InvariantCulture));
}

Then

public class Money { public decimal Value { get; set; } }

public class WithMoney { public Money MyMoney { get; set; } }

static void Main(string[] args)
{
    var serializeOptions = new JsonSerializerOptions
    {
        WriteIndented = true,
        Converters = { new MoneyJsonConverter() }
    };


    var m = new WithMoney { MyMoney = new Money { Value = 6.66m } };

    var json = JsonSerializer.Serialize(m, serializeOptions);
    Console.WriteLine(json);

    var m2 = JsonSerializer.Deserialize<WithMoney>(json, serializeOptions);
    Console.WriteLine(m2.MyMoney.Value);
}

This prints:

{
  "MyMoney": "6.66"
}
6.66

The usings are

using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

and the reference is How to write custom converters for JSON serialization (marshalling) in .NET. This is not for this case, but please note that there are two patterns for creating custom converters depending on types handled (e.g. List<T> vs List<DateTime>). Please see the link for more details.

tymtam
  • 31,798
  • 8
  • 86
  • 126