webapi frombody xml blank with declaration - c#

I have a asp.net webapi v2 running katana and using XmlSerializer with a very simple custom Serialization implementation:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.Formatters.XmlFormatter.SetSerializer<SalesOrder>(new XmlSerializer<SalesOrder>());
XmlSerializer is just a simple implementation of the to System.Runtime.Serialization.XmlObjectSerializer abstract class
When calling a Post like this:
public HttpResponseMessage Post([FromBody]SalesOrder value)
{
return value == null || value.SignZoneCustomerNumber.IsNullOrEmpty()
? Request.CreateResponse(HttpStatusCode.BadRequest)
: Request.CreateResponse(HttpStatusCode.Accepted, _service.CreateOrder(value, Request.GetCorrelationId()));
}
The value is null when the xml contains a declaration like this
<?xml version="1.0" encoding="ISO-8859-15"?>
Strip out the declaration and send the post it desearilizes fine.
Any idea why the declaration causes deseriazation to fail?
The Model is defined like this:
[XmlRoot("Order")]
[Serializable]
public class SalesOrder : BaseOrder<SalesOrderLineItem>
{
public string PoNumber { get; set; }
public DateTime? PoDate { get; set; }
...more properties...
}
Thanks
UPDATE
Turns out it was the encoding that caused the problem
this header works:
<?xml version="1.0" encoding="UTF-8"?>
other values like UTF-16 and ISO-8859-1 fail
any idea why?

Related

Swagger UI incorrectly displays properties in lower-case for XML requests

I have a basic .NET 6 web API project with Swagger and I have a dummy HttpPost endpoint that should accept both JSON and XML. As far as I see, model bindings for JSON are case insensitive but for XML it's not, and here comes my problem.
In Program.cs I have the following settings for XML serialization:
builder.Services.AddControllers(options =>
{
options.InputFormatters.Add(new XmlSerializerInputFormatter(options));
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});
My endpoint:
[HttpPost]
public async Task<DummyDto> PostDummyData(DummyDto dummyDto)
{
return await Task.FromResult(dummyDto);
}
My DTO:
public class DummyDto
{
public int Id { get; set; }
public string Name { get; set; }
}
Default example in Swagger UI for application/xml and text/xml:
<?xml version="1.0" encoding="UTF-8"?>
<DummyDto>
<id>0</id>
<name>string</name>
</DummyDto>
As you can see my class name is correct, but my properties are lowercase by default and after a long search I still have no idea how to display my property attributes correctly, or how to disable case sensitivity for XML model binding.
By adding the [XmlElement] attribute to the properties I was able to make it work with lowercase, but I don't like this workaround. Is there any generic solution that I could use for this problem?
By default Web API serializes the fields of types that have a [Serializable] attribute (e.g. Version).
You can add this line of code in Program.cs to stop the Web API from doing this:
builder.Services.AddControllersWithViews().
AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
Test Result:

AspNetCore Swagger/Swashbuckle how to modify xml schema requests

I have implemented Swagger/Swashbuckle in my AspNetCore 2.1 app and it's working great. However some of my API models are based on complex WCF XML services and use a few System.Xml.Serialization annotations for adding namespaces and changing the names of properties.
When these models are viewed on the Swagger page they are missing the namespaces and ignore any attribute name changes. Therefore the swagger default requests won't deserialize when posted to my controller.
On the other hand the JSON requests work fine.
Consider these two classes;
public class test
{
[System.Xml.Serialization.XmlElementAttribute(Namespace = "http://www.example.com/ns/v1")]
public test_c ct1 { get; set; }
public string t2 { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.com/ns/v1")]
public class test_c
{
[System.Xml.Serialization.XmlElementAttribute("my-new-name")]
public string tc1 { get; set; }
public string tc2 { get; set; }
}
When serialized as XML we get something like;
<?xml version="1.0" encoding="UTF-8"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ct1 xmlns="http://www.example.com/ns/v1">
<my-new-name>aaa</my-new-name>
<tc2>xxxxxx</tc2>
</ct1>
<t2>bbb</t2>
</test>
This is what is the xml that is expected as the request.
However, the swagger sample request shows as;
<?xml version="1.0" encoding="UTF-8"?>
<test>
<ct1>
<tc1>string</tc1>
<tc2>string</tc2>
</ct1>
<t2>string</t2>
</test>
Which will not deserialize when posted.
So now to the point. All I need/hope to do is modify the swagger request xml schema -- while not affecting the JSON request schema (I don't even know if they are -or can be- separate).
I thought this would be simple but I'm lost sea of swashbuckle options & setup.
I was hoping that I could simply assign the aspnet xml serializer to deserialize a provided request object. Or implement an ISchemaFilter or IOperationsFilter?
If someone could point me in the right direction I'd be forever grateful.
ok, well I'll answer this myself.
I did end up implementing ISchemaFilter. So to answer this question using the same models as in the question, I first created my own implementation of ISchemaFilter and just hardcoded in the checks for the required changes (in reality i'm going to have a big dictionary<> of class and property changes).
The "Schema" class allows us to add XML only meta to the model class or any of its properties.
public class MyRequestISchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
if (schema.Type == "object"){
if (context.SystemType == typeof(test_c))
{
schema.Xml = new Xml()
{
Namespace = "http://www.example.com/ns/v1"
};
foreach (var prop in schema.Properties)
{
if (prop.Key == "tc1")
{
prop.Value.Xml = new Xml()
{
Name = "my-new-name"
};
}
}
}
}
}
}
Then we wire-up this filter in our AddSwaggerGen service configure call at startup.
services.AddSwaggerGen(c =>
{
c.SchemaFilter<MyRequestISchemaFilter>();
...
Here is the sample request XML that the filter will produce;
<?xml version="1.0" encoding="UTF-8"?>
<test>
<ct1 xmlns="http://www.example.com/ns/v1">
<my-new-name>string</my-new-name>
<tc2>string</tc2>
</ct1>
<t2>string</t2>
</test>
It's missing the root level XMLSchema namespaces but the Schema::Xml prop doesn't support multiple namespaces. In any case the XML deserializes fine as long as I don't use those namespaces.
I might be able to add them if I add properties with a namespace then set them as Attributes of the root element (which the Xml prop does handle). Haven't tried that though.

ASP.NET MVC XML serialization is not working

In my Startup.ConfigureServices method I have:
services.AddMvc(setupAction =>
{
setupAction.ReturnHttpNotAcceptable = true;
setupAction.OutputFormatters.Add(new XmlSerializerOutputFormatter());
setupAction.InputFormatters.Add(new XmlSerializerInputFormatter());
});
I've also tried:
services.AddMvc(setupAction =>
{
setupAction.ReturnHttpNotAcceptable = true;
setupAction.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
setupAction.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
});
My POST method's signature looks like:
[HttpPost("xunit")]
public IActionResult PostXunitResults([FromBody] XunitResult results)
XunitResult looks like:
[Serializable]
[XmlRoot("assemblies")]
public class XunitResult
{
[XmlIgnore]
public const string MimeType = "application/xml";
[XmlAttribute("timestamp")]
public string Timestamp { get; set; }
[XmlElement("assembly")]
public List<Assembly> Assemblies { get; set; }
}
[Serializable]
[XmlType("assembly")]
public class Assembly
{
...
With this setup I can run tests to check that my model deserializes the XML correctly:
[Fact]
public void WhenYouDeserializeXunitResultsIntoAnXunitResultsObject_ThenTheObjectIsPopulatedCorrectly()
{
var serializer = new XmlSerializer(typeof(XunitResult));
XunitResult results;
using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(_xunitResults)))
{
results = (XunitResult)serializer.Deserialize(stream);
}
results.Assemblies.Should().NotBeNull();
}
... which it does.
When I try to use XunitResult as a [FromBody] argument to the post method, the model is not rendered correctly in Swagger:
Swagger does not display XunitResult correctly in the example, it renders the root as 'XunitResult' instead of 'assemblies' as defined in the XMLRootAttribute on XunitResult.
Also, the same model does not render the Timestamp property in XunitResult as an attribute, it renders it as an element. This is just an example. The other XmlAttributes in the class are rendered as elements as well, and not as attributes like they should be.
The assemblies element is rendered as an assembly should be if it were inside of the assemblies element.
view screenshot for the above 2 items here
The XML model should look similar to this:
<?xml version="1.0" encoding="utf-8"?>
<assemblies timestamp="02/27/2018 14:32:47">
<assembly name="API.Test.dll" environment="64-bit .NET Standard [collection-per-class, parallel (8 threads)]" test-framework="xUnit.net 2.3.1.3858" run-date="2018-02-27" run-time="14:32:47" total="9" passed="5" failed="3" skipped="1" time="0.161" errors="0">
<errors />
<collection total="1" passed="0" failed="1" skipped="0" name="Test collection for API.Test.API.Results.TestResultPost2" time="0.000">
<test name="API.Test.API.Results.TestResultPost2.TestWeWontGetTo" type="API.Test.API.Results.TestResultPost2" method="TestWeWontGetTo" time="0" result="Fail">
<failure exception-type="System.Exception">
<message><![CDATA[System.Exception : exception in setup]]></message>
<stack-trace><![CDATA[ at API.Test.API.Results.TestResultPost2..ctor() in /Users//Documents///API.Test/Controller/TestResultsPost.cs:line 60]]></stack-trace>
</failure>
</test>
</collection>
...
And when I try to post XML according to the model in swagger, OR if I try to post valid XML, the results variable in the PostXunitResults method is null.
If I inspect the body of the call via Request.Body, then the first part of the XML that I posted is gone, only the last part is in the Body stream.
Any help to figure this out would be great!

XmlSerializer ignores [XmlAttribute] in WebApi

I have a WebApi that returns a simple object, but when I'm forcing it to return as XML (Accept: application/xml) it ignores the [XmlAttribute] attribute I've set on the object.
This is my object:
public class Foo
{
[XmlAttribute]
public string Bar { get; set; }
}
And I return it like this in the code:
[RoutePrefix("api/mytest")]
public class MyTestController : System.Web.Http.ApiController
{
[HttpGet]
[Route("gettest")]
public Foo GetTest()
{
return new Foo() { Bar = "foobar" };
}
}
The resulting XML is:
<Foo>
<Bar>foobar</Bar>
</Foo>
Whereas I would expect it to be returned like this:
<Foo Bar="foobar">
</Foo>
Why does the XmlSerializer used by WebApi ignore the [XmlAttribute] attribute, and how can I make it work like I want to?
Try setting this global configuration value in your WebApi to true
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
By default Web API uses DataContractSerializer in XmlMediaTypeFormatter.
This worked for me... no need to change global configuration.
var configuration = new HttpConfiguration();
configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
return Request.CreateResponse(HttpStatusCode.OK, myObjectToSerialize, configuration);

Error in xml file using the XmlSerializer

I have an website with a administrator page. On this page a user is able to create, update or delete employees. I want to save these employees in a xml file using the XmlSerializer of C#.
The employee class looks like this:
public class Employee
{
public int id { get; set; }
public String lastname { get; set; }
public String firstname { get; set; }
public String enterdate { get; set; }
public int salarylevel { get; set; }
}
However, I get an error while reading the xml file and do not know what to do:
In System.InvalidOperationException is an exception of type
"System.Xml.dll" occurred, but this was not in the user code
processed.
Additional information: error in XML document (0,0).
Translated from german.
I created the project as an empty WebAPI and I have an EmployeeController that looks like this:
public class EmployeeController : ApiController
{
List<Employee> employeelist = new List<Employee>();
[HttpGet]
public IEnumerable<Employee> getEmployees()
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));
FileStream stream = new FileStream(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "employees.xml", FileMode.Open);
employeelist = (List<Employee>)serializer.Deserialize(stream);
return employeelist;
}
}
The error occurs at: employeelist = (List<Employee>)serializer.Deserialize(stream);
The controller gets called with JQuery and the getJSON method using the url http://localhost:5267/api/employee/. All employees should be saved in a xml file that is called employees.xml which contains only one line at the moment:
<?xml version="1.0" encoding="utf-8" ?>
What I expected to happen was, that I get an empty list because there are no related xml elements in the file and therefore the page shows no employees.
I guess that I messed it up because I created the xml file manually and without the serializer or that the serializer cannot handle the one line xml. Does someone know how I should solve this problem?
Creating the xml using Serialize is not required for successful serialization, but is useful to determine if your xml looks like it should. A serialized empty collection should look more like:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEmployee xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />

Categories

Resources