我知道这里和其他地方都有人问过这个问题,但我没有看到一个简单的答案。或者至少,我还没能找到。
简而言之,我有一个接受.Net的端点。使用(在启动中):
services.AddControllers().AddXmlSerializerFormatters();
我想把它绑定到一个类上。示例:
[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; }
}
传入:
<Person><Name>John</Name><Id>123</Id></Person>
工作正常。然而,一旦命名空间开始发挥作用,它要么无法绑定模型:
<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>
或者可以绑定模型,但不绑定属性:
<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:
XmlTextReader rdr = new XmlTextReader(s);
rdr.Namespaces = false;
但微软建议从.Net Framework2.0开始不使用XmlTextReader,因此更愿意坚持使用.Net核心(在本例中为5)。
发布于 2021-05-19 12:48:22
您可以使用自定义的InputFormatter,这里有一个演示:
XmlSerializerInputFormatterNamespace:
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"] });
}
}
人员:
public class Person
{
public string Name { get; set; }
public int Id { get; set; }
}
启动:
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true; // false by default
options.InputFormatters.Insert(0, new XmlSerializerInputFormatterNamespace());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
结果:
发布于 2021-05-21 20:39:49
因此,为了能够在不考虑名称空间的情况下将XML模型绑定到类,我创建了新的InputFormatter。我使用XmlTextReader是为了忽略名称空间。微软建议使用XmlReader而不是XmlTextReader。但是由于XmlTextReader仍然存在(在.Net 6.0预览版3中),我将暂时使用它。
只需像这样创建一个继承自XmlSerializerInputFormatter的输入格式化程序:
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);
}
}
}
然后将其添加到输入格式化程序中,如下所示:
services.AddControllers(o =>
{
o.InputFormatters.Add(new XmlNoNameSpaceInputFormatter(o));
})
.AddXmlSerializerFormatters();
现在,无论传入的XML中是否有名称空间,我们都可以对Person或任何其他类进行建模绑定。感谢@艺艺-你
https://stackoverflow.com/questions/67587610
复制相似问题