首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在asp net核心web api中进行模型绑定时忽略XML命名空间。

在asp net核心web api中进行模型绑定时忽略XML命名空间。
EN

Stack Overflow用户
提问于 2021-05-18 21:51:08
回答 2查看 254关注 0票数 1

我知道这里和其他地方都有人问过这个问题,但我没有看到一个简单的答案。或者至少,我还没能找到。

简而言之,我有一个接受.Net的端点。使用(在启动中):

代码语言:javascript
运行
复制
services.AddControllers().AddXmlSerializerFormatters();

我想把它绑定到一个类上。示例:

代码语言:javascript
运行
复制
[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
    [HttpPost]
    [Consumes("application/xml")]
    [ApiConventionMethod(typeof(DefaultApiConventions), nameof(DefaultApiConventions.Post))]
    public async Task<ActionResult> PostPerson([FromBody] Person person)
    {
        return Ok();
    }
}

// Class/Model
[XmlRoot(ElementName = "Person")]
public class Person
{
    [XmlElement(ElementName = "Name")]
    public string Name { get; set; }

    [XmlElement(ElementName = "Id")]
    public int Id { get; set; }
}

传入:

代码语言:javascript
运行
复制
<Person><Name>John</Name><Id>123</Id></Person>

工作正常。然而,一旦命名空间开始发挥作用,它要么无法绑定模型:

代码语言:javascript
运行
复制
<Person xmlns="http://example.org"><Name>John</Name><Id>123</Id></Person>
<Person xmlns="http://example.org"><Name>John</Name><Id xmlns="http://example.org">123</Id></Person>

或者可以绑定模型,但不绑定属性:

代码语言:javascript
运行
复制
<Person><Name xmlns="http://example.org">John</Name><Id>123</Id></Person>
<Person><Name xmlns="http://example.org">John</Name><Id xmlns="http://example.org">123</Id></Person>

等。

我理解名称空间。我确实意识到我可以在XML属性中为根和元素设置名称空间。然而,我(我们)有几十个调用者,他们都随心所欲地设置自己的名称空间。我希望避免有几十个不同版本的Person类(在本例中)(每个调用者一个)。我的意思也是说,如果调用者更改了他们的名称空间,我将不得不更新调用者的特定版本并重新部署代码。

那么,如何在不考虑名称空间的情况下将传入的XML模型绑定到Person实例?

我已经做了一些覆盖/创建输入格式化程序的测试,使用XmlTextReader和设置namespaces=false:

代码语言:javascript
运行
复制
        XmlTextReader rdr = new XmlTextReader(s);
        rdr.Namespaces = false;

但微软建议从.Net Framework2.0开始不使用XmlTextReader,因此更愿意坚持使用.Net核心(在本例中为5)。

EN

回答 2

Stack Overflow用户

发布于 2021-05-19 12:48:22

您可以使用自定义的InputFormatter,这里有一个演示:

XmlSerializerInputFormatterNamespace:

代码语言:javascript
运行
复制
public class XmlSerializerInputFormatterNamespace : InputFormatter, IInputFormatter, IApiRequestFormatMetadataProvider

    {
        public XmlSerializerInputFormatterNamespace()
        {
            SupportedMediaTypes.Add("application/xml");
        }
        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
        {
            var xmlDoc = await XDocument.LoadAsync(context.HttpContext.Request.Body, LoadOptions.None, CancellationToken.None);
            Dictionary<string, string> d = new Dictionary<string, string>();
            foreach (var elem in xmlDoc.Descendants())
            {
                d[elem.Name.LocalName] = elem.Value;
            }
            return InputFormatterResult.Success(new Person { Id = Int32.Parse(d["Id"]), Name = d["Name"] }); 
        }
       

    }

人员:

代码语言:javascript
运行
复制
public class Person
{
    public string Name { get; set; }

    public int Id { get; set; }
}

启动:

代码语言:javascript
运行
复制
services.AddMvc(options =>
            {
                options.RespectBrowserAcceptHeader = true; // false by default
                options.InputFormatters.Insert(0, new XmlSerializerInputFormatterNamespace());
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
           .AddXmlSerializerFormatters()
          .AddXmlDataContractSerializerFormatters();

结果:

票数 0
EN

Stack Overflow用户

发布于 2021-05-21 20:39:49

因此,为了能够在不考虑名称空间的情况下将XML模型绑定到类,我创建了新的InputFormatter。我使用XmlTextReader是为了忽略名称空间。微软建议使用XmlReader而不是XmlTextReader。但是由于XmlTextReader仍然存在(在.Net 6.0预览版3中),我将暂时使用它。

只需像这样创建一个继承自XmlSerializerInputFormatter的输入格式化程序:

代码语言:javascript
运行
复制
public class XmlNoNameSpaceInputFormatter : XmlSerializerInputFormatter
{
    private const string ContentType = "application/xml";
    public XmlNoNameSpaceInputFormatter(MvcOptions options) : base(options)
    {
        SupportedMediaTypes.Add(ContentType);
    }

    public override bool CanRead(InputFormatterContext context)
    {
        var contentType = context.HttpContext.Request.ContentType;
        return contentType.StartsWith(ContentType);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var type = GetSerializableType(context.ModelType);
        var request = context.HttpContext.Request;

        using (var reader = new StreamReader(request.Body))
        {
            var content = await reader.ReadToEndAsync();
            Stream s = new MemoryStream(Encoding.UTF8.GetBytes(content));

            XmlTextReader rdr = new XmlTextReader(s);
            rdr.Namespaces = false;
            var serializer = new XmlSerializer(type);
            var result = serializer.Deserialize(rdr);
            return await InputFormatterResult.SuccessAsync(result);
        }
    }
}

然后将其添加到输入格式化程序中,如下所示:

代码语言:javascript
运行
复制
        services.AddControllers(o => 
        {
            o.InputFormatters.Add(new XmlNoNameSpaceInputFormatter(o));
        })
        .AddXmlSerializerFormatters();

现在,无论传入的XML中是否有名称空间,我们都可以对Person或任何其他类进行建模绑定。感谢@艺艺-你

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67587610

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档