2 Stimmen

Wie kann ich Json.NET dazu bringen, Int32 in eine URI umzuwandeln

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:

  1. Wie benenne ich "accountID" in "accountUri" um (und "productID" in "productUri")?

  2. Wie rendere ich die Werte dieser Eigenschaften (ersetze "{}" durch das Ergebnis von ToString() der Wrapper-Klasse)?

  3. 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".

4voto

jmsb Punkte 4788

1) Verwenden Sie den Parameter PropertyName des JsonProperty-Attributs, wie zum Beispiel:

[JsonProperty(PropertyName = "accountUri")]
public AccountIdentifier AccountID { get; set; }

2) Die {} beinhalten die Inhalte der Klassen AccountIdentifier und ProductIdentifier. Sie müssen einen benutzerdefinierten Json Converter für die Order-Klasse schreiben, um die (De-)Serialisierungsinhalte anzupassen.

Hier finden Sie die relevante JSON.NET-Dokumentation.

3) Sie können dies auch über JsonProperty festlegen.

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]

EDIT: Hier ist ein Teil eines benutzerdefinierten JsonConvert für die Order-Klasse. Es verwendet nicht die oben genannten Attribute, sondern behandelt es manuell. Falls jemand eine bessere (und umfassendere) Lösung bereitstellen kann, bitte tun Sie das.

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);
    }
}

1voto

David Peden Punkte 16390

Da Sie sich entschieden haben, das Adaptermuster in Betracht zu ziehen, finden Sie hier eine Beispielimplementierung mit Tests:

[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);
    }
}

Wenn Sie Fragen haben, posten Sie diese bitte mit Kommentaren, und ich werde alles annotieren, was weiterer Erklärung bedarf.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X