web-dev-qa-db-ja.com

名前空間を失うようにMVC4 WebApiXmlSerializerを調整します

ストレージにPOCOクラスでEFを使用するMVCWebAPIに取り組んでいます。私がやりたいのは、XMLから名前空間を削除して、エンドポイントが名前空間なしでxmlオブジェクトを返し、受け入れるようにすることです。 (jsonは問題なく動作します)

<ACCOUNT xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Platform.Services.AccountService.Data">
<id>22</id>
<City i:nil="true"/>
<Country i:nil="true"/>
<Email>[email protected]</Email>
<Phone i:nil="true"/> ...

これを機能させたい

 <ACCOUNT>
    <id>22</id>
    <City i:nil="true"/>
    <Country i:nil="true"/>
    <Email>[email protected]</Email>
    <Phone i:nil="true"/> ...

うまくいけば、POCOをたくさんの属性で飾る必要はありません。

私はこれに対するテストソリューションを設定しました、そして確かに、これらの方法は打撃を受けています(私のシステムの他の問題であるに違いありません)。とにかく-私がこのソリューションを使用して得た結果はこれです:

<ArrayOfAccount>
<Account>
<id>22</id>
<name>TestAcc</name>
<parentid xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance" d3p1:nil="true"/>
<status_id xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance" d3p1:nil="true"/>
<Email>[email protected]</Email>
</Account>
</ArrayOfAccount>

上のスキーマを削除しましたが、プロパティがめちゃくちゃになっています:(これが サンプルプロジェクト へのリンクです

13
Marty

ここでのこの答えは的を射ています ASP.NET Web APIからXMLの名前空間を削除します 。\

POCOをまったく装飾したくない場合は、最初のオプションを使用してください。

config.Formatters.XmlFormatter.UseXmlSerializer = true;

オプション2を使用する場合は、System.Runtime.Serializationへの参照を追加する必要がある場合があります。

Acceptが正しく設定されたこのような投稿を想定すると、次のようになります。

GET http:// ANY OLD SERVER/api/foos/5 Accept:application/xml

コントローラー

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Web.Http;

namespace CutomXmlFormater.Controllers
{
//[DataContract(Namespace = "")]
public class Foo
{
    //[DataMember]
    public string Bar { get; set; }
}

public class FoosController : ApiController
{
    // GET api/foos/5
    public Foo Get(int id)
    {
        return new Foo() { Bar = "Test" };
    }
}

}

Config(App_Start/WebApiConfig)

//(Use this is you don't go the data contact and model annotation route)
config.Formatters.XmlFormatter.UseXmlSerializer = true;

結果

いずれか(注釈とデータ連絡付き):

<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Bar>Test</Bar></Foo>

または(XMLシリアライザールートを使用):

<Foo xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Bar>Test</Bar></Foo>
13
Mark Jones

多分あなたはこれで試すことができます:

デフォルトのXmlFormatterを独自のものに置き換えます。

_GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
_

そして、次のように、シリアル化中に空の名前空間を指定して、XmlSerializerを使用して実装します。

_public CustomXmlFormatter()
{
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
    Encoding = new UTF8Encoding(false, true);
}

protected override bool CanReadType(Type type)
{
    if (type == (Type)null)
        throw new ArgumentNullException("type");

    if (type == typeof(IKeyValueModel))
        return false;

    return true;
}

protected override bool CanWriteType(Type type)
{
    return true;
}

protected override Task OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
{
    return Task.Factory.StartNew(() =>
            {
                using (var streamReader = new StreamReader(stream, Encoding))
                {
                    var serializer = new XmlSerializer(type);
                    return serializer.Deserialize(streamReader);
                }
            });
}

protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, System.Net.TransportContext transportContext)
{
    var serializer = new XmlSerializer(type);
    return Task.Factory.StartNew(() =>
            {
                using (var streamWriter = new StreamWriter(stream, Encoding))
                {
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                    ns.Add("", "");
                    serializer.Serialize(streamWriter, value, ns);
                }
            });
    }
}
_

カスタムXMLシリアライザーが ここ から盗まれたため、テストされていません。

これにより、名前空間を記述せずにオブジェクトをシリアル化する必要があります。それが機能するかどうかはわかりませんOOTB逆シリアル化のために、XmlSerializer.Deserialize()イベントを提供するオーバーロード を試して処理する必要があるかもしれません nknownElement または nknownNode イベント。

6
Boris B.

MVC 4をいじってからしばらく経ちましたが、デフォルトのフォーマッターを次のようにXmlSerializerに置き換えることになりました。

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = GetSerializeSettings();
        GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
    }

    internal JsonSerializerSettings GetSerializeSettings()
    {
        return new JsonSerializerSettings
                           {
                               Formatting = Formatting.Indented,
                               ContractResolver = new CamelCasePropertyNamesContractResolver(),
                               Converters = new List<JsonConverter> { new IsoDateTimeConverter() }
                           };
    }

これは役立つかもしれません...あなたがやりたくないと言ったPOCOの属性を使用してプロパティ名もカスタマイズしたことは知っていますが、それはキャメルケースにしたかったからです。

1
BlakeH

MVC Webapi 5に対するBorisの回答をカスタマイズしました。次のhttpヘッダーのいずれかを使用して、CustomFormatterを使用して結果をレンダリングします。

受け入れる:application/xml

受け入れる:text/xml

WebApiConfig.cs:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
    }
}

CustomXmlFormatter.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Serialization;

namespace Custom.Formatter
{
    public class CustomXmlFormatter: MediaTypeFormatter
    {
        private  UTF8Encoding encoder;

        public CustomXmlFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
            encoder = new UTF8Encoding(false, true);
        }

        public override bool CanReadType(Type type)
        {
            if (type == (Type)null)
                throw new ArgumentNullException("type");

            //Type filtering
            if (type == typeof(SendEmailMessageResponse) || type == typeof(SendSmsMessageResponse))
                return true;
            else
                return false;
        }

        public override bool CanWriteType(Type type)
        {
            return true;
        }

        public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
        {
            return Task.Factory.StartNew(() =>
                    {
                        using (var streamReader = new StreamReader(stream, encoder))
                        {
                            var serializer = new XmlSerializer(type);
                            return serializer.Deserialize(streamReader);
                        }
                    });
        }

        public override Task WriteToStreamAsync(Type type, object value, Stream stream,    HttpContent content, TransportContext transportContext)
        {
            var serializer = new XmlSerializer(type);
            return Task.Factory.StartNew(() =>
                    {
                        using (var streamWriter = new StreamWriter(stream, encoder))
                        {
                            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                            ns.Add("", "");
                            serializer.Serialize(streamWriter, value, ns);
                        }
                    });
        }
    }
}
0
Y P