I have created a DtoClass and referenced VersionRange class from Nuget.Versioning :
DtoClass:
public class DependencyDto
{
public string Name { get; set; }
[JsonProperty("VersionRange")]
[JsonConverter(typeof(VersionRangeConverter))]
public VersionRange VersionRange { get; set; }
}
I have implemented simple JsonConverter in order to use Object's value in string representation format on swagger UI.
JsonConverter :
public class VersionRangeConverter : JsonConverter<VersionRange>
{
public override void WriteJson(JsonWriter writer, VersionRange value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override VersionRange ReadJson(JsonReader reader, Type objectType, VersionRange existingValue, bool hasExistingValue, JsonSerializer serializer)
{
Requires.NotNull(serializer, nameof(JsonSerializer));
VersionRange versionRange = null;
var versionRangeObject = serializer.Deserialize(reader);
if (versionRangeObject is null)
{
return null;
}
else if (versionRangeObject is string)
{
if (!VersionRange.TryParse(versionRangeObject.ToString(), out versionRange))
{
throw new Exception();
}
}
else
{
try
{
dynamic ver = JsonConvert.DeserializeObject(versionRangeObject.ToString());
NuGetVersion maxVersion = versionRange.MaxVersion;
NuGetVersion minVersion = versionRange.MinVersion;
bool isMinInclusive = versionRange.IsMinInclusive;
bool isMaxInclusive = versionRange.IsMaxInclusive;
string originalString = versionRange.OriginalString;
versionRange = new VersionRange(minVersion, isMinInclusive, maxVersion, isMaxInclusive, null, originalString);
}
catch
{
throw;
}
}
return versionRange;
}
}
I have created one more Dependency class but without JsonConverter attribute over VersionRange property as I want to save whole Object to Cosmos DB. I have used automapper to map these two classes. This works fine and it gets saved in db as expected. see below. Document in Cosmos DB :
"Dependencies": [
{
"Name": "string",
"VersionRange": {
"IsFloating": false,
"Float": null,
"OriginalString": "[1.0.0, 2.0.0)",
"HasLowerBound": true,
"HasUpperBound": true,
"HasLowerAndUpperBounds": true,
"IsMinInclusive": true,
"IsMaxInclusive": false,
"MaxVersion": "2.0.0",
"MinVersion": "1.0.0"
}
}
]
Now when i am trying to read response from cosmos db by casting it to a class which does not have JsonConverter attribute over a VersionRange property. I am getting error. Code to write to cosmos and cast repose :
ResourceResponse<Document> response = await this.DocumentDbClient.WriteAsync(doc.Id, this.CollectionName, doc).ConfigureAwait(false);
DependencyDoc dependencyDoc= (DependencyDoc)(dynamic)response.Resource;
Exception:
"Message": "Unable to find a constructor to use for type NuGet.Versioning.VersionRange. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Module.Dependencies[0].VersionRange.IsFloating', line 1, position 285.",
You can accomplish this by using the VersionRangeFactory
public class VersionRangeConverter : JsonConverter<VersionRange>
{
public override void WriteJson(JsonWriter writer, VersionRange value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override VersionRange ReadJson(JsonReader reader, Type objectType, VersionRange existingValue, bool hasExistingValue, JsonSerializer serializer)
{
// Deserialize as Dictionary to be able to access the values by key
var dict = serializer.Deserialize<Dictionary<string, object>>(reader);
// Utilize the `VersionRangeFactory`
// Add your validations here if key exists, value is string etc.
// This is just for demo purpose
// ... or use one of the other constructors from the factory to build the full object from the dictionary which is now in your hand.
var versionRange = VersionRange.Parse(dict["OriginalString"] as string, dict["IsFloating"] == "true");
return versionRange;
}
}
Here is a small working example
If you have VersionRange
from response body as string
you can also add a handling to your converter to support full deserialized object and string object.
public override VersionRange ReadJson(JsonReader reader, Type objectType, VersionRange existingValue, bool hasExistingValue, JsonSerializer serializer)
{
VersionRange versionRange = null;
try
{
var dict = serializer.Deserialize<Dictionary<string, object>>(reader);
if(dict != null)
{
versionRange = VersionRange.Parse(dict["OriginalString"] as string, dict["IsFloating"] == "true");
}
}
catch
{
// Handling if response body contains only the string version declaration
var strValue = serializer.Deserialize<string>(reader);
if(!string.IsNullOrWhiteSpace(strValue))
{
versionRange = VersionRange.Parse(strValue);
}
}
return versionRange;
}
Next working example