Im C#-Code möchte ich meine Int32-Eigenschaften so verwenden, wie sie entworfen wurden - als int, aber wenn sie mit Json.NET in Json konvertiert werden, möchte ich, dass sie als URI serialisiert werden, damit ich nicht alle meine Modelle einfach für die json-Ausgabe zu einem anderen Modell mappen muss. z.B. ein vereinfachtes Modell:
public class Order
{
public int? AccountID { get; set; }
public int ProductID { get; set; }
public decimal Total { get; set; }
}
Ich möchte, dass dies so gerendert wird:
{ "accountUri": "/account/123", "productUri": "/product/456", "total": 789.01 }
Beachten Sie, dass die Großschreibung und die Umbenennung der Eigenschaften geändert wurden.
Wenn AccountID null ist, muss das Json wie folgt gerendert werden:
{ "productUri": "/product/456", "total": 789.01 }
Im C#-Code möchte ich die Eigenschaften immer noch wie ein normales int verwenden - daher überlege ich, Int-Operatorüberschreibungen zu verwenden.
Ich möchte keine Attribute auf den Modelleigenschaften verwenden, aber ich bin bereit, eine Wrapper-Klasse für das Int32 zu verwenden, und ich habe nichts dagegen, Attribute auf der Wrapper-Klasse zu verwenden, wenn erforderlich.
Der folgende Code ist weit von der Antwort entfernt, aber Sie verstehen den Grundgedanken:
public class Order
{
public AccountIdentifier AccountID { get; set; }
public ProductIdentifier ProductID { get; set; }
public decimal Total { get; set; }
}
public abstract class IdentifierBase
{
private readonly string _uriPrefix;
private int? _value;
protected IdentifierBase(string uriPrefix, int? value)
{
_uriPrefix = uriPrefix;
_value = value;
}
public override string ToString()
{
if (_value.HasValue)
return _uriPrefix + _value.Value;
return null;
}
// fügen Sie hier Int-Operatorüberschreibungen ein.
}
public class AccountIdentifier : IdentifierBase
{
public AccountIdentifier(int? value)
: base("/account/", value)
{
}
}
public class ProductIdentifier : IdentifierBase
{
public ProductIdentifier(int? value)
: base("/product/", value)
{
}
}
[Test]
public void JsonConvert()
{
var order = new Order
{
AccountID = new AccountIdentifier(123),
ProductID = new ProductIdentifier(456),
Total = 789.01M
};
using (var stringWriter = new StringWriter())
{
var writer = new JsonTextWriter(stringWriter) {Formatting = Formatting.None};
var settings = new JsonSerializerSettings();
var serializer = JsonSerializer.Create(settings);
// Fügen Sie den Eigenschaften Camel Case hinzu.
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
serializer.Serialize(writer, order);
writer.Flush();
var json = stringWriter.GetStringBuilder().ToString();
Console.Write(json);
}
}
Dies ergibt:
{"accountID":{},"productID":{},"total":789.01}
Drei Fragen:
-
Wie benenne ich "accountID" in "accountUri" um (und "productID" in "productUri")?
-
Wie rendere ich die Werte dieser Eigenschaften (ersetze "{}" durch das Ergebnis von ToString() der Wrapper-Klasse)?
-
Wie entferne ich eine Eigenschaft vollständig, wenn sie null ist?
Danke.
EDIT: Obwohl es ziemlich viel Arbeit ist, für jedes Modell einen Konverter zu schreiben, spart es das Schreiben von zwei Zuweisungen. Hier sind meine Proof-of-Concept-Tests:
[TestFixture]
public class MyPoC
{
public class OrderJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
var order = value as Order;
if (order.AccountID.HasValue)
{
writer.WritePropertyName("accountUri");
serializer.Serialize(writer, "/account/" + order.AccountID);
}
writer.WritePropertyName("productUri");
serializer.Serialize(writer, "/product/" + order.ProductID);
writer.WritePropertyName("total");
serializer.Serialize(writer, order.Total);
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var order = new Order();
var jsonObject = JObject.Load(reader);
order.AccountID = jsonObject.GetNullableIntFromUri("accountUri");
order.ProductID = jsonObject.GetIntFromUri("productUri");
order.Total = jsonObject["total"].Value();
return order;
}
public override bool CanConvert(Type objectType)
{
return typeof(Order).IsAssignableFrom(objectType);
}
}
[Test]
public void JsonConvert_Is_Successful()
{
var order = new Order
{
AccountID = 123,
ProductID = 456,
Total = 789.01M
};
var json = JsonConvert.SerializeObject(order, Formatting.None, new OrderJsonConverter());
Console.WriteLine(json);
var deserialized = JsonConvert.DeserializeObject(json, new OrderJsonConverter());
Console.WriteLine("AccountID: {0}", deserialized.AccountID);
Console.WriteLine("ProductID: {0}", deserialized.ProductID);
Console.WriteLine("Total: {0}", deserialized.Total);
}
}
}
public static class JObjectExtensions
{
public static int GetIntFromUri(this JObject jsonObject, string propertyName)
{
var id = jsonObject.GetNullableIntFromUri(propertyName);
return id.Value;
}
public static int? GetNullableIntFromUri(this JObject jsonObject, string propertyName)
{
var uri = jsonObject[propertyName].ToObject();
var s = Regex.Replace(uri, @".*/(\d+)$", "$1");
int id;
if (int.TryParse(s, out id))
{
return id;
}
return null;
}
}
AUSGABE:
{"accountUri":"/account/123","productUri":"/product/456","total":789.01}
AccountID: 123
ProductID: 456
Total: 789.01
Zusätzliche Arbeit wäre, zu validieren, ob die URI korrekt ist und nicht nur ein generisches "die ID am Ende der URI abgreifen".