如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?

Posted

技术标签:

【中文标题】如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?【英文标题】:How to post a dynamic JSON property to a C# ASP.Net Core Web API using MongoDB? 【发布时间】:2017-12-09 16:44:45 【问题描述】:

似乎 MongoDB 的优势之一是能够将您需要的任何内容存储在 Object 类型属性中,但似乎不清楚如何通过 C# Web 从客户端 ajax 帖子获取动态属性信息MongoDB 的 API。

我们希望允许管理员创建一个带有标题和开始日期/时间的事件,但我们也希望允许用户使用响应式表单添加自定义表单字段,以满足他们想要的任何内容,例如 T 恤尺寸或膳食偏好。 .. 无论用户将来想出什么。然后,当有人注册该事件时,他们会将 EventID 和自定义字段发布到 Web API。

我们可以拥有一个包含 _id、event_id、reg_time 和 form_fields 的 Event MongoDB 集合,其中 form_fields 是存储动态数据的对象类型。

所以我们想用自定义的 FormsFields 发布这个 JSON 的变体:

变体 1:


    "EventId": "595106234fccfc5fc88c40c2",
    "RegTime":"2017-07-21T22:00:00Z",
    "FormFields": 
        "FirstName": "John",
        "LastName": "Public",
        "TShirtSize": "XL"
    

变体 2:


    "EventId": "d34f46234fccfc5fc88c40c2",
    "RegTime":"2017-07-21T22:00:00Z",
    "FormFields": 
        "Email": "John.Public@email.com",
        "MealPref": "Vegan"
    

我想要一个带有 Post 操作的 EventController,它采用映射到上述 JSON 的自定义 C# EventReg 对象。

事件控制器:

[HttpPost]
public void Post([FromBody]EventReg value)

    eventService.AddEventRegistration(value);

EventReg 类:

public class EventReg

    public EventReg()
    
        FormFields = new BsonDocument();
    
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventRegId  get; set; 

    [BsonElement("EventId")]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventId  get; set; 

    [BsonElement("reg_time")]
    public DateTime RegTime
    
        set; get;
    

    [BsonElement("form_fields")]
    public MongoDB.Bson.BsonDocument FormFields  get; set; 

事件服务

public string AddEventRegistration(EventReg eventReg)

    this.database.GetCollection<EventReg>("event_regs").InsertOne(eventReg);
    return eventReg.EventRegId;
 

现在,如果我发布到控制器,我的 EventReg 为 null,因为它一定不知道如何将我的 JSON FormFields 属性映射到 BsonDocument。

FormFields 可以使用什么类型? 我可以将 FormFields 属性设置为 BsonDocument,是否有一种简单的方法可以将 Web API 参数映射到该属性? 是否有示例说明某些自定义序列化程序在这种情况下如何工作?

我们也许可以使用动态类型并循环通过发布的属性,但这看起来很难看。我还从这里的帖子中看到了 JToken 解决方案,但这看起来也很丑陋。

如果 MongoDB 打算像这样动态使用,难道不应该有一个干净的解决方案将动态数据传递给 MongoDB 吗?有什么想法吗?

【问题讨论】:

【参考方案1】:

JToken 示例用于获取数据,但在检索时会导致浏览器和 Postman 抛出错误并显示警告,指示内容被作为文档读取,但它是 application/json 格式。我看到 FormFields 属性被返回为 "TShirtSize":"XL" 所以在序列化过程中双括号可能是一个问题。

我最终在 System.Dynamic 命名空间中使用了 .NET ExpandoObject。 ExpandoObject 与 MongoDB BsonDocument 兼容,因此序列化会像您期望的那样自动完成。所以不需要奇怪的代码来手动处理问题中的 JToken 示例等属性。

我仍然认为,如果可能的话,应该使用更强类型的 C# 表示,但是如果您必须允许任何 JSON 内容通过 Web API 以自定义 C# 类作为输入发送到 MongoDB,那么 ExpandoObject 应该这样做诀窍。

看看下面 EventReg 类的 FormFields 属性是如何变成 ExpandoObject 的,并且没有代码可以手动处理保存到 MongoDB 的对象的属性。

这是用标准类型属性和动态 FormFields 属性手动填充对象的原始有问题且过于复杂的 JToken 代码:

[HttpPost]
public void Post([FromBody]JToken token)

    if (token != null)
    
        EventReg eventReg = new EventReg();
        if (token.Type == Newtonsoft.Json.Linq.JTokenType.Object)
        
            eventReg.RegTime = DateTime.Now;
            foreach (var pair in token as Newtonsoft.Json.Linq.JObject)
            
                if (pair.Key == "EventID")
                
                    eventReg.EventId = pair.Value.ToString();
                
                else if (pair.Key == "UserEmail")
                
                    eventReg.UserEmail = pair.Value.ToString();
                
                else
                
                    eventReg.FormFields.Add(new BsonElement(pair.Key.ToString(), pair.Value.ToString()));
                 
            
        
        //Add Registration:
        eventService.AddEventRegistration(eventReg);
    

使用 ExpandoObject 消除了对所有这些代码的需要。请参阅下面的最终代码。 Web API 控制器现在是 1 行而不是 30 行代码。此代码现在可以毫无问题地从上述问题中插入和返回 JSON。

事件控制器:

[HttpPost]
public void Post([FromBody]EventReg value)

    eventService.AddEventRegistration(value);

EventReg 类:

public class EventReg

    public EventReg()
    
       FormFields = new ExpandoObject();
    
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventRegId  get; set; 

    [BsonElement("event_id")]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventId  get; set; 

    [BsonElement("user_email")]
    public string UserEmail  get; set; 

    [BsonElement("reg_time")]
    public DateTime RegTime get; set; 

    [BsonElement("form_fields")]
    public ExpandoObject FormFields  get; set; 

事件服务:

public string AddEventRegistration(EventReg eventReg)

    this.database.GetCollection<EventReg>("event_regs").InsertOne(eventReg);
    return eventReg.EventRegId;

【讨论】:

【参考方案2】:

在 ASP.NET Core 3.0+ 中,Newtonsoft.Json 不再是默认的 JSON 序列化程序。因此我会使用 JsonElement:

[HttpPost("general")]
public IActionResult Post([FromBody] JsonElement elem)
        
    var title = elem.GetProperty("title").GetString();
    ...

【讨论】:

【参考方案3】:

如果您不确定要发送的 Json 的类型,即如果您正在处理动态 json。那么下面的方法将起作用。 接口:

    [HttpPost]
    [Route("demoPath")]
    public void DemoReportData([FromBody] JObject Jsondata)

来自 .net 核心应用程序的 Http 客户端调用:

using (var httpClient = new HttpClient())


    return await httpClient.PostAsJsonAsync(serviceUrl,
    new  demoClass()
                 
                   id= "demoid",
                   name= "demo name",
                   place= "demo place"
                  ;).ConfigureAwait(false);

【讨论】:

【参考方案4】:

您可以将FormFields定义为字符串,将JSON转成字符串后以字符串格式发送数据到API:

"\"FirstName\":"John\",\"LastName\":\"Public\",\"TShirtSize\":\"XL\""

然后在您的控制器中将字符串解析为 BsonDocument

BsonDocument.Parse(FormFields);

我会使用 AutoMapper 来自动化 dto 和文档之间的转换

【讨论】:

以上是关于如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB。如何更新数组内的json属性

如何在 JavaScript 中使用 Mongoose 动态更新文档?

如何将 JSON 数据格式化为动态生成对象属性的 C# 对象

mongodb怎么插入多个文档

如何使用 spark/scala 将 json 字符串格式化为 MongoDB 文档样式?

如何使用 Python 将 MongoDB 的 bsondump 转换为 JSON?