C# Map JSON Serialization and Deserialization to Reference Object Using Object Property

53 Views Asked by At

I have two JSON files: One for Locations, another for Objects at a location.

locations.json =>

[
  {
    "Name": "Location#1",
    "X": 0,
    "Y": 20
  },
  {
    "Name": "Location#2",
    "X": 0,
    "Y": 19
  },
  ...
]

objects.json ==>

[
  {
    "Name": "Piano",
    "CurrentLocation": "Location#1"
  },
  {
    "Name": "Violin",
    "CurrentLocation": "Location#2"
  },
  ...
]

The objects.json references the locations instances using the location names.

I have two classes that this deserializes to (or serializes from):

public class ObjectOfInterest
{
    [JsonPropertyName("Name")]  
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("CurrentLocation")]
    public LocationNode CurrentLocation { get; set; } = new()
}

public class Location
{
    [JsonPropertyName("Name")]  
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("X")]
    public float X { get; set; }

    [JsonPropertyName("Y")]
    public float Y { get; set; }
}

How do I create a custom JSONSerializer or Converter that takes the string Location name JSON attribute, and assigns the correct location instance to the Objects class?

2

There are 2 best solutions below

0
SashaZd On BEST ANSWER

It turns out that the JSON Converter was overkill. I ended up indexing the locations by their Name. And then making an additional public string type that mapped to the indexed private Location type (renamed to _currentLocation since we can't have two properties with the same name) I needed.

private Location _currentLocation = new();

[JsonPropertyName("CurrentLocation")]
public string CurrentLocation {
    get => _currentLocation.Name; 
    set {
        _currentLocation = LocationIndexer.Locations[value];
    }
}
0
Damien On

Add a JsonConverterAttribute to the CurrentLocation property

public class ObjectOfInterest
{
    [JsonPropertyName("Name")]
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("CurrentLocation")]
    [JsonConverter(typeof(LocationConverter))]
    public Location CurrentLocation { get; set; } = new();
}

The converter should store the locations indexed by their name:

public class LocationConverter : JsonConverter<Location>
{
    private readonly Dictionary<string, Location> _locationDictionary = new();

    public LocationConverter(IEnumerable<Location> locations)
    {
        foreach (var location in locations)
        {
            _locationDictionary[location.Name] = location;
        }
    }

    public override Location Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string locationName = reader.GetString();
        if (_locationDictionary.TryGetValue(locationName, out Location location))
        {
            return location;
        }

        throw new KeyNotFoundException($"Location '{locationName}' not found.");
    }

    public override void Write(Utf8JsonWriter writer, Location value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.Name);
    }
}

Finaly, you can use the LocationConverter like that:

var locations = JsonSerializer.Deserialize<Location[]>(locationsJson);
var options = new JsonSerializerOptions();
options.Converters.Add(new LocationConverter(locations));
var objects = JsonSerializer.Deserialize<ObjectOfInterest[]>(objectsJson, options);