【问题标题】:How to write a WebService that will accept Json with "Multi Type Property"?如何编写一个接受具有“多类型属性”的 Json 的 WebService?
【发布时间】:2020-09-22 02:23:14
【问题描述】:

上下文:
Cust 有一个发送以下 json 的服务。他可以轻松更改该查询的目标,但不能更改它自己的查询。

我必须构建一个接受如下 JSON 查询的 WebService。 虽然我处理 Json 没有问题,但我在尝试定义将接受这样的查询的方法/接口时遇到问题。

问题来自 Houses > Things:它是一个字符串字典,“objectThing”其中“objectThing”的属性值可能包含多种类型。
例如:

  • 整数,"Value": 42
  • 字符串,"Value": "Catty"
  • 字符串数组,"Value": ["Book1", "Book2", "Book3"]
  • 对象,对象类型的有限列表
    "Value": 
          {
            "PeopleId": "1234ABCD",
            "Name": "John"
          }
    
  • 对象数组,对象类型有限列表的数组
    "Value": [
          {
            "PeopleId": "1234ABCD",
            "Name": "John"
          },
          {
            "PeopleId": "0000AAAA",
            "Name": "Doe"
          }
     ]
    
    价值对我来说不是动态的。它在我可以定义的有限类型列表中。

Json 示例:

{
    "RootID"    : "0123456",
    "FooID"     : "0123456",    
    "BarID"     : "0123456",
    "Houses"    :[
        {
            "OwnerId"   : "0123456",
            "Date"      : 1890895600000,
            "Location"  : {
                "Latitude"  : -1,
                "Longitude" : -1
            },

            "Things" :{
                "1" :{
                    "Label": "Books",                   
                    "Type" : "List",
                    "Value": ["Book1", "Book2", "Book3"]
                },
                "2" :{
                    "Label": "Cat",                 
                    "Type" : "Text",
                    "Value": "Catty"
                },
                "3" :{
                    "Label": "A Number",                    
                    "Type" : "Int",
                    "Value": 42
                },
                "4" :{
                    "Label": "Peoples",                 
                    "Type" : "People",
                    "Value": [
                        {
                          "PeopleId": "1234ABCD",
                          "Name": "John"
                        },
                        {
                          "PeopleId": "0000AAAA",
                          "Name": "Doe"
                        }
                    ]
                }               
            }
        },
        {       
            "OwnerId"   : "111111",
            "Things" :{}
        },
        {       
            "OwnerId"   : "000001",
            "Things" :{}
        }   
    ]
}

以及类定义,如果我要将这个 Json 反序列化为适当的类型:

using System;
using System.Collections.Generic;

using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public partial class QueryRoot
{
    [JsonProperty("RootID")]
    public string RootId { get; set; }

    [JsonProperty("FooID")]
    public string FooId { get; set; }

    [JsonProperty("BarID")]
    public string BarId { get; set; }

    [JsonProperty("Houses")]
    public List<House> Houses { get; set; }
}

public partial class House
{
    [JsonProperty("OwnerId")]
    public string OwnerId { get; set; }

    [JsonProperty("Date", NullValueHandling = NullValueHandling.Ignore)]
    public long? Date { get; set; }

    [JsonProperty("Location", NullValueHandling = NullValueHandling.Ignore)]
    public Location Location { get; set; }

    [JsonProperty("Things")]
    public Dictionary<string, Thing> Things { get; set; }
}

public partial class Location
{
    [JsonProperty("Latitude")]
    public long Latitude { get; set; }

    [JsonProperty("Longitude")]
    public long Longitude { get; set; }
}

public partial class Thing
{
    [JsonProperty("Label")]
    public string Label { get; set; }

    [JsonProperty("Type")]
    public string Type { get; set; }

    [JsonProperty("Value")]
    public ThingValue Value { get; set; }
}

public partial class ValueClass
{
    [JsonProperty("PeopleId")]
    public string PeopleId { get; set; }

    [JsonProperty("Name")]
    public string Name { get; set; }
}

public partial struct ValueElement
{
    public string String;
    public ValueClass ValueClass;

    public static implicit operator ValueElement(string String) => new ValueElement { String = String };
    public static implicit operator ValueElement(ValueClass ValueClass) => new ValueElement { ValueClass = ValueClass };
}

public partial struct ThingValue
{
    public List<ValueElement> AnythingArray;
    public long? Integer;
    public string String;

    public static implicit operator ThingValue(List<ValueElement> AnythingArray) => new ThingValue { AnythingArray = AnythingArray };
    public static implicit operator ThingValue(long Integer) => new ThingValue { Integer = Integer };
    public static implicit operator ThingValue(string String) => new ThingValue { String = String };
}

public partial class QueryRoot
{
    public static QueryRoot FromJson(string json) => JsonConvert.DeserializeObject<QueryRoot>(json, QuickType.Converter.Settings);
}

public static class Serialize
{
    public static string ToJson(this QueryRoot self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}

internal static class Converter
{
    public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
    {
        MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
        DateParseHandling = DateParseHandling.None,
        Converters =
        {
            ThingValueConverter.Singleton,
            ValueElementConverter.Singleton,
            new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
        },
    };
}

internal class ThingValueConverter : JsonConverter
{
    public override bool CanConvert(Type t) => t == typeof(ThingValue) || t == typeof(ThingValue?);

    public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
    {
        switch (reader.TokenType)
        {
            case JsonToken.Integer:
                var integerValue = serializer.Deserialize<long>(reader);
                return new ThingValue { Integer = integerValue };
            case JsonToken.String:
            case JsonToken.Date:
                var stringValue = serializer.Deserialize<string>(reader);
                return new ThingValue { String = stringValue };
            case JsonToken.StartArray:
                var arrayValue = serializer.Deserialize<List<ValueElement>>(reader);
                return new ThingValue { AnythingArray = arrayValue };
        }
        throw new Exception("Cannot unmarshal type ThingValue");
    }

    public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
    {
        var value = (ThingValue)untypedValue;
        if (value.Integer != null)
        {
            serializer.Serialize(writer, value.Integer.Value);
            return;
        }
        if (value.String != null)
        {
            serializer.Serialize(writer, value.String);
            return;
        }
        if (value.AnythingArray != null)
        {
            serializer.Serialize(writer, value.AnythingArray);
            return;
        }
        throw new Exception("Cannot marshal type ThingValue");
    }

    public static readonly ThingValueConverter Singleton = new ThingValueConverter();
}

internal class ValueElementConverter : JsonConverter
{
    public override bool CanConvert(Type t) => t == typeof(ValueElement) || t == typeof(ValueElement?);

    public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
    {
        switch (reader.TokenType)
        {
            case JsonToken.String:
            case JsonToken.Date:
                var stringValue = serializer.Deserialize<string>(reader);
                return new ValueElement { String = stringValue };
            case JsonToken.StartObject:
                var objectValue = serializer.Deserialize<ValueClass>(reader);
                return new ValueElement { ValueClass = objectValue };
        }
        throw new Exception("Cannot unmarshal type ValueElement");
    }

    public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
    {
        var value = (ValueElement)untypedValue;
        if (value.String != null)
        {
            serializer.Serialize(writer, value.String);
            return;
        }
        if (value.ValueClass != null)
        {
            serializer.Serialize(writer, value.ValueClass);
            return;
        }
        throw new Exception("Cannot marshal type ValueElement");
    }

    public static readonly ValueElementConverter Singleton = new ValueElementConverter();
}

我已经有一个处理 Json 的 WCF 服务。它工作正常,问题是声明将接受这种查询的方法/接口。 如果 WCF Web 服务是一个限制因素,或者(ASP.NET/Core)Web API 提供了更简单的路径,那么它是受欢迎的。

【问题讨论】:

标签: c# json web-services wcf asp.net-web-api


【解决方案1】:

您可以接收 JSON 字符串并将其转换为对象。这是一个演示:

      [WebMethod]
    public string HelloWorld()
    {
        Stream s = HttpContext.Current.Request.InputStream; 
        byte[] b = new byte[s.Length];
         s.Read(b, 0, (int)s.Length);
         string jsontext = Encoding.UTF8.GetString(b);
        var productProperty = JsonHelper.JsonDeserialize<School>(jsontext); //Deserialize JSON strings to objects
        return "Hello World";
    }

这是WebService中的方法。

[DataContract]
public class School
{
    [DataMember]
    public int ClassroomId { set; get; }
    [DataMember]
    public List<Student> StudentList { set; get; }
}
[DataContract]
public class Student
{
    [DataMember]
    public int StudentId { set; get; }
    [DataMember]
    public string StudentName { set; get; }
}

这是JSON字符串要转换的对象。

    public class JsonHelper
{
    public static string JsonSerializer<T>(T t)
    {
        var ser = new DataContractJsonSerializer(typeof(T));
        var ms = new MemoryStream();
        ser.WriteObject(ms, t);
        string jsonString = Encoding.UTF8.GetString(ms.ToArray());
        ms.Close();
        return jsonString;
    }
    public static T JsonDeserialize<T>(string jsonString)
    {
        var ser = new DataContractJsonSerializer(typeof(T));
        var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
        var obj = (T)ser.ReadObject(ms);
        return obj;
    }
}

为了将 JSON 字符反序列化为对象,有很多开源类库。我用的是.net 3.5或以上版本自带的DatacontractJsonSerializer。我写了一个JsonHelper类。

【讨论】:

  • WebInvoke(Method = "POST") 并使用带有RequestContext.RequestMessage.GetBody 的正文。是我没有考虑过的事情。并要求将其发送为Content-Type:text/plain
  • 我也会给HttpContext.Current.Request.InputStream
  • 好吧,您可以预计在此之前几周会遇到沙盒。这部分似乎没有被最终客户使用,虽然它在 100% 的覆盖范围内,但我不确定是否有人会活得够久才能看到它。我会尝试一下个人发展的工作。但是没找到时间和精力。
猜你喜欢
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 2016-03-28
  • 1970-01-01
  • 1970-01-01
  • 2016-07-07
  • 1970-01-01
  • 2012-09-08
相关资源
最近更新 更多