简体   繁体   English

接受原始 JSON Asp.net 核心

[英]Accepting raw JSON Asp.net core

I have a asp.net core method, I want it to accept RAW json, I do not and will not always know the schema of the json object, so I am using dynamic types with dot notation.我有一个 asp.net 核心方法,我希望它接受 RAW json,我不知道也不会总是知道 json 对象的模式,所以我使用带点符号的dynamic types

This method works when I string the json escaping each character.当我将 json 字符串转义为每个字符时,此方法有效。 I have tried to use the json body directly, but this did not work.我曾尝试直接使用 json 主体,但这不起作用。 So it seems my option were to Serialize and then Deserialize the json.所以看起来我的选择是Serialize然后Deserialize json。 ( very redundant) but it seems to throw error any other way if I try to use the JSON body directly. (非常多余)但如果我尝试直接使用 JSON 主体,它似乎会以任何其他方式抛出错误。

In the debugger, everything seems to work with the Serialize and Deserialize of the object / string, but throws an error on the id(property) when I try to cast the object to string and gives the error.在调试器中,一切似乎都适用于对象/字符串的序列化和反序列化,但是当我尝试将对象转换为字符串并给出错误时,会在 id(property) 上引发错误。 (In the debugger I am able to see the Id correctly though). (虽然在调试器中我能够正确地看到 Id)。

({Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert type 'System.Text.Json.JsonElement' to 'string')}

I really do not see why it gives the type as a string yet cannot convert it.我真的不明白为什么它将类型作为字符串提供但无法转换它。 I have even tried to remove the casting, and still receive this error.我什至尝试删除演员表,但仍然收到此错误。

public IActionResult Post([FromBody] ExpandoObject requestInput)
{
    try
    {
//Makes a JSON String
        var stringObject = (string) JsonSerializer.Serialize(requestInput);
        DateTime time = DateTime.UtcNow;
// Recreated the Json Object
        dynamic requestObject = JsonSerializer.Deserialize<ExpandoObject>(stringObject); 
// Throws Error here, yet it shows Value as the correct Id number (Value: Type String)
        string reqObject = (string) requestObject.Id;

So there is no support for ExpandoObject in .NET Core, yet.因此,.NET Core 中尚不支持ExpandoObject MS says that maybe it will be added in .NET 5.0. MS 说它可能会被添加到 .NET 5.0 中。 Until then, you can use this JsonConverter I found on a thread .在那之前,你可以使用我在一个线程上找到的这个JsonConverter I will post the code here in case that thread goes away.如果线程消失,我将在此处发布代码。

You can use it like this:你可以这样使用它:

[HttpPost, Route("testPost")]
public IActionResult TestPost([FromBody] object obj) // just use "object"
{
    // object is: { "hello":"world" }

    var myDynamic = JsonSerializer.Deserialize<dynamic>(
        JsonSerializer.Serialize(obj), new JsonSerializerOptions
        {
            Converters = { new DynamicJsonConverter() }
        });

    var test = (string)myDynamic.hello;
    // test will equal "world"

    return Ok();
}

Here is the converter:这是转换器:

/// <summary>
/// Temp Dynamic Converter
/// by:tchivs@live.cn
/// </summary>
public class DynamicJsonConverter : JsonConverter<dynamic>
{
    public override dynamic Read(ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {

        if (reader.TokenType == JsonTokenType.True)
        {
            return true;
        }

        if (reader.TokenType == JsonTokenType.False)
        {
            return false;
        }

        if (reader.TokenType == JsonTokenType.Number)
        {
            if (reader.TryGetInt64(out long l))
            {
                return l;
            }

            return reader.GetDouble();
        }

        if (reader.TokenType == JsonTokenType.String)
        {
            if (reader.TryGetDateTime(out DateTime datetime))
            {
                return datetime;
            }

            return reader.GetString();
        }

        if (reader.TokenType == JsonTokenType.StartObject)
        {
            using JsonDocument documentV = JsonDocument.ParseValue(ref reader);
            return ReadObject(documentV.RootElement);
        }
        // Use JsonElement as fallback.
        // Newtonsoft uses JArray or JObject.
        JsonDocument document = JsonDocument.ParseValue(ref reader);
        return document.RootElement.Clone();
    }

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
    {
        object? result = null;
        switch (jsonElement.ValueKind)
        {
            case JsonValueKind.Object:
                result = ReadObject(jsonElement);
                break;
            case JsonValueKind.Array:
                result = ReadList(jsonElement);
                break;
            case JsonValueKind.String:
                //TODO: Missing Datetime&Bytes Convert
                result = jsonElement.GetString();
                break;
            case JsonValueKind.Number:
                //TODO: more num type
                result = 0;
                if (jsonElement.TryGetInt64(out long l))
                {
                    result = l;
                }
                break;
            case JsonValueKind.True:
                result = true;
                break;
            case JsonValueKind.False:
                result = false;
                break;
            case JsonValueKind.Undefined:
            case JsonValueKind.Null:
                result = null;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
        return result;
    }

    private object? ReadList(JsonElement jsonElement)
    {
        IList<object?> list = new List<object?>();
        foreach (var item in jsonElement.EnumerateArray())
        {
            list.Add(ReadValue(item));
        }
        return list.Count == 0 ? null : list;
    }
    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
    {
        // writer.WriteStringValue(value.ToString());
    }
}

Edited To Add:编辑添加:

Here is a much slicker way to handle dynamic using the converter above as pointed out by Aluan in the comments.正如Aluan在评论中指出的那样,这是使用上面的转换器处理dynamic的更巧妙的方法。 In your Startup.cs class, add this:在您的Startup.cs类中,添加以下内容:

services.AddControllers().AddJsonOptions(options =>
    options.JsonSerializerOptions.Converters.Add(new DynamicJsonConverter()));

Then you don't have to do any goofy stuff in your controller.然后你不必在你的控制器中做任何愚蠢的事情。 You can just set the body parameter as dynamic and it magically works:您可以将 body 参数设置为动态,它神奇地工作:

[HttpPost, Route("testPost")]
public IActionResult TestPost([FromBody] dynamic obj)
{
    // object is: { "hello":"world" }

    var test = (string)obj.hello;
    // test will equal "world"

    return Ok();
}

Way nicer!好多了!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM