如何编写一个接受具有“多类型属性”的 Json 的 WebService?

Posted

技术标签:

【中文标题】如何编写一个接受具有“多类型属性”的 Json 的 WebService?【英文标题】:How to write a WebService that will accept Json with "Multi Type Property"? 【发布时间】: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 提供了更简单的路径,那么它是受欢迎的。

【问题讨论】:

相关:***.com/questions/11465858/…. Anonymous and Weakly-Typed Objects with 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 Clas-s-roomId  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% 的覆盖范围内,但我不确定是否有人会活得够久才能看到它。我会尝试一下个人发展的工作。但是没找到时间和精力。

以上是关于如何编写一个接受具有“多类型属性”的 Json 的 WebService?的主要内容,如果未能解决你的问题,请参考以下文章

ajax传递json,然后服务器接受json的代码编写

Phpmyadmin 仅接受来自具有多个输入值的 json 数组中的一个条目

如何在 express 和 bodyParser 中接受 application/csp-report 作为 json?

Spring MVC + JSON = 406 不可接受

接受/返回 XML/JSON 请求和响应 - Spring MVC

创建一个 aws 端点以接受 json 格式并将其存储到 DS