在C#代码中,我想使用我设计的Int32属性 - 作为int,但是当使用Json.NET转换为Json时,我希望它们被序列化为URI,所以我不必为了json输出将我的所有模型映射到另一个模型。例如一个简化的模型:
public class Order
{
public int? AccountID { get; set; }
public int ProductID { get; set; }
public decimal Total { get; set; }
}
我希望这样渲染:
{ "accountUri": "/account/123", "productUri": "/product/456", "total": 789.01 }
请注意,外壳和属性重命名已更改。
如果AccountID为null,那么json必须像这样呈现:
{ "productUri": "/product/456", "total": 789.01 }
在C#代码中我仍然喜欢像普通int一样使用这些属性 - 所以我想使用int运算符覆盖。
我不想在模型属性上使用属性,但我很乐意为Int32使用包装类,如果需要,请不要在包装类上使用属性。< / p>
以下代码是一个公平的答案,但你得到了要点:
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;
}
// insert int operator overrides here.
}
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);
// Camel case the properties.
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
serializer.Serialize(writer, order);
writer.Flush();
var json = stringWriter.GetStringBuilder().ToString();
Console.Write(json);
}
}
输出:
{"accountID":{},"productID":{},"total":789.01}
三个问题:
如何重命名&#34; accountID&#34; to&#34; accountUri&#34; (和&#34; productID&#34;到&#34; productUri&#34;)?
如何渲染这些属性的值(用包装类的ToString()结果替换&#34; {}&#34;
如果属性为null,如何删除属性?
感谢。
编辑:尽管为每个模型编写转换器需要做很多工作,但它确实可以节省写入两个映射器。这是我的概念测试证明:
[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<decimal>();
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<Order>(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<string>();
var s = Regex.Replace(uri, @".*/(\d+)$", "$1");
int id;
if (int.TryParse(s, out id))
{
return id;
}
return null;
}
}
输出:
{"accountUri":"/account/123","productUri":"/product/456","total":789.01}
AccountID: 123
ProductID: 456
Total: 789.01
额外的工作是验证uri是否正确,而不仅仅是通用&#34;从uri&#34;的末尾撕掉id。
答案 0 :(得分:4)
1)使用JsonProperty属性的PropertyName参数,例如:
[JsonProperty(PropertyName = "accountUri")]
public AccountIdentifier AccountID { get; set; }
2){}是AccountIdentifier和ProductIdentifier类的内容。您需要为Order类编写自定义Json转换器,以便自定义(反)序列化内容。
以下是相关的JSON.NET documentation。
3)您也可以通过JsonProperty设置它。
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
编辑:以下是Order类的自定义JsonConvert的一部分。它不使用上面提到的属性,而是手动处理它。如果其他人可以提供更好(更完整)的解决方案,请做。
public class OrderJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
var obj = value as Order;
writer.WritePropertyName("accountUri");
serializer.Serialize(writer, obj.AccountID.ToString());
writer.WritePropertyName("productUri");
serializer.Serialize(writer, obj.ProductID.ToString());
writer.WritePropertyName("Total");
serializer.Serialize(writer, obj.Total);
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(Order).IsAssignableFrom(objectType);
}
}
答案 1 :(得分:1)
由于您已决定考虑使用适配器模式,因此这是一个带有测试的示例实现:
[TestFixture]
public class When_serializing_Order
{
[SetUp]
public void SetUp()
{
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
}
[TestCase(123, 456, 789.01, "{\"accountUri\":\"/account/123\",\"productUri\":\"/product/456\",\"total\":789.01}")]
[TestCase(null, 456, 789.01, "{\"productUri\":\"/product/456\",\"total\":789.01}")]
public void Should_render_exact_json(int? accountId, int productId, decimal total, string expectedJson)
{
var order = new Order
{
AccountID = accountId,
ProductID = productId,
Total = total
};
string jsonOrder = JsonConvert.SerializeObject(new OrderAdapter(order));
Assert.That(jsonOrder, Is.EqualTo(expectedJson));
}
}
public class Order
{
public int? AccountID { get; set; }
public int ProductID { get; set; }
public decimal Total { get; set; }
}
public class OrderAdapter
{
private readonly Uri _accountUri;
private readonly Uri _productUri;
private readonly decimal _total;
public OrderAdapter(Order order)
{
_accountUri = order.AccountID != null ? CreateRelativeUri("account", order.AccountID.Value) : null;
_productUri = CreateRelativeUri("product", order.ProductID);
_total = order.Total;
}
public Uri AccountUri { get { return _accountUri; } }
public Uri ProductUri { get { return _productUri; } }
public decimal Total { get { return _total; } }
private Uri CreateRelativeUri(string resourceType, int id)
{
return new Uri(String.Format("/{0}/{1}", resourceType, id), UriKind.Relative);
}
}
如果您有任何疑问,请发表评论,并注明需要进一步解释的任何内容。