0

クラスのインスタンスをAuthenticateUserOutputWCF Web サービスから返します。

したがって、次の方法があります。

public override AuthenticateUserOutput AuthenticateUser(AuthenticateUserInput AuthenticateUserInput)

AuthenticateUserOutput は自動生成されます。

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.5420")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="xxxx")]
public partial class AuthenticateUserOutput : WS2MethodOutput
{

    private bool authenticationResultField;

    private UserContext userContextField;

    /// <remarks/>
    public bool AuthenticationResult
    {
        get
        {
            return this.authenticationResultField;
        }
        set
        {
            this.authenticationResultField = value;
        }
    }

    /// <remarks/>
    public UserContext UserContext
    {
        get
        {
            return this.userContextField;
        }
        set
        {
            this.userContextField = value;
        }
    }
}

としてデシリアライズできる必要がありますがAuthenticateUserOutput、機能していません。

テストとして、 をインスタンス化しAuthenticateUserOutput、シリアル化し、逆シリアル化を試みました。

InvalidOperationException で失敗します。

シリアル化された Xml は次のとおりです。

<?xml version="1.0" encoding="utf-16"?>
<AuthenticateUserOutput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="xxxx">
  <Response xmlns="xxxx">
    <IsValid>true</IsValid>
    <Success>true</Success>
  </Response>
  <AuthenticationResult xmlns="xxxx">true</AuthenticationResult>
  <UserContext xmlns="xxxx">
  </UserContext>
</AuthenticateUserOutput>

シリアライゼーションとデシリアライゼーションのコードは次のとおりです。

    public static string ToXml(this object input)
    {
        string output;
        XmlSerializer serializer = new XmlSerializer(input.GetType());
        StringBuilder sb = new StringBuilder();
        using (TextWriter textWriter = new StringWriter(sb))
        {
            serializer.Serialize(textWriter, input);
        }
        output = sb.ToString();
        return output;
    }

と:

    public static T FromXml<T>(this string input)
    {
        T output;

        XmlSerializer serializer = new XmlSerializer(typeof(T));

        output = (T)serializer.Deserialize(new StringReader(input));

        return output;
    }

例外の正確な詳細:

System.InvalidOperationException occurred
  Message="<AuthenticateUserOutput xmlns='xxxx'> was not expected."
  Source="qkxd8dd-"
  StackTrace:
       at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderAuthenticateUserOutput.Read31_AuthenticateUserOutput()
  InnerException: 

そのため、シリアル化された独自の Xml を逆シリアル化することはできません。

誰でも理由がわかりますか?

ありがとう、

J1M.

更新: テスト コードは次のとおりです。

        AuthenticateUserOutput test = new AuthenticateUserOutput();

        test.AuthenticationResult = true;
        test.Response = new ResponseType();
        test.Response.Exception = null;
        test.Response.IsValid = true;
        test.Response.Success = true;
        test.Response.ValidationErrors = null;
        test.UserContext = new UserContext();

        string serializedXml = test.ToXml();

        AuthenticateUserOutput deserializedString = serializedXml.FromXml<AuthenticateUserOutput>();

UPDATE2 : デシリアライザーは Mark Gravell のおかげで動作します

OK、デシリアライズ時に名前空間を正しく取得するためにこれを追加したのを忘れていました:

public partial class WS2MethodInput
{
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces _xmlns;

    /// <summary>
    /// Constructor for WS2MethodInput that sets up default namespaces
    /// </summary>
    public WS2MethodInput()
    {
        _xmlns = new XmlSerializerNamespaces();
        _xmlns.Add("", "xxxx");
    }
}

これで、シリアル化された Xml は、このメッセージの先頭にあったとおりになります。これがないと、デシリアライザーは機能しますが、AuthenticateUserOutput に名前空間がありません。

<?xml version="1.0" encoding="utf-16"?>
<AuthenticateUserOutput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="xxxx">
  <Response xmlns="xxxx">
    <IsValid>true</IsValid>
    <Success>true</Success>
  </Response>
  <AuthenticationResult xmlns="xxxx">true</AuthenticationResult>
  <UserContext xmlns="xxxx">
  </UserContext>
</AuthenticateUserOutput>

xmlns="xxxx"の最後に注意してくださいAuthenticateUserOutput

問題は、次のいずれかがなければ、その Xml を他のコードで使用できないことです。

1)XDocumentにロードし、名前空間を追加して、逆シリアル化する必要があるときに削除します2)正規表現などの文字列置換で同じことを行います

どちらも私は本当に好きではありません。実際、それは恐ろしいことです!8X

4

1 に答える 1

0

問題は、ルート要素にxmlnsを強制するようにXmlSerializerNamespacesを設定することでした。

それを取り除けばうまくいきます。

ただし、名前空間がないため、シリアル化されたXmlを他の場所で使用することはできません。

それに焦点を当てた新しい質問を始めます。

ありがとう、

J1M。

アップデート:

OK、わかったので、新しい質問を書くつもりはありません。

次のXSDが与えられた場合:

<xs:schema xmlns="urn:www-test-com:testservice" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:www-test-com:testservice" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="AuthenticateUserInput">
        <xs:annotation>
            <xs:documentation>Used to provide user credentials to the AuthenticateUser method</xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Username" type="xs:string"/>
                <xs:element name="Password" type="xs:string"/>
                <xs:element name="Method" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

svcutilはこのようなものを生成しますが、そのXSDに似たものを吐き出すツールはたくさんあると思います。

[Serializable()]
[XmlType(AnonymousType = true, Namespace = "urn:www-test-com:testservice")]
public class AuthenticateUserInput
{
    public string Username { get; set; }
    public string Password { get; set; }
    public string Method { get; set; }
}

ここで、「デフォルト」XmlSerializerコードを使用します。

    static string ToXml(object input)
    {
        string output;

        XmlSerializer serializer = new XmlSerializer(input.GetType());
        StringBuilder sb = new StringBuilder();

        using (XmlWriter xw = XmlWriter.Create(sb))
        {
            serializer.Serialize(xw, input);
        }
        output = sb.ToString();
        return output;
    }

次のXmlを取得します。

<AuthenticateUserInput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Username xmlns="urn:www-test-com:testservice">sa</Username>
    <Password xmlns="urn:www-test-com:testservice">Password1</Password>
    <Method xmlns="urn:www-test-com:testservice">Plain</Method>
</AuthenticateUserInput>

これはスキーマに準拠していません(検証は失敗します。これが現時点で私たちを殺しているものです)

OK、それで、に変更するための参照を見つけ、[XmlType]大まか[XmlRoot]なテストが機能しました。生成されたクラスを変更すると、次の準拠Xmlが得られます。

<AuthenticateUserInput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:www-test-com:testservice">
    <Username>sa</Username>
    <Password>Password1</Password>
    <Method>Plain</Method>
</AuthenticateUserInput>

しかし、自動生成されたすべてのクラスを手動で変更したくはありません。しばらくして、次の解決策を見つけました。

    static string ToXml(object input)
    {
        string output;

        XmlSerializer serializer = CreateSerializer(input.GetType());
        StringBuilder sb = new StringBuilder();

        var settings = new XmlWriterSettings() { OmitXmlDeclaration = true, Encoding =  Encoding.UTF8, Indent = true };

        using (XmlWriter xw = XmlWriter.Create(sb, settings))
        {
            serializer.Serialize(xw, input);
        }
        output = sb.ToString();
        return output;
    }

    static T FromXml<T>(string input)
    {
        T output;

        XmlSerializer serializer = CreateSerializer(typeof(T));

        output = (T)serializer.Deserialize(new StringReader(input));

        return output;
    }

    private static XmlSerializer CreateSerializer(Type incomingType)
    {
        XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
        XmlAttributes newAttributes = new XmlAttributes();

        newAttributes.XmlRoot = new XmlRootAttribute();
        newAttributes.XmlRoot.Namespace = ((XmlTypeAttribute)incomingType.GetCustomAttributes(typeof(XmlTypeAttribute), true)[0]).Namespace;
        attrOverrides.Add(incomingType, newAttributes);

        XmlSerializer serializer = new XmlSerializer(incomingType, attrOverrides);
        return serializer;
    }

ご覧のとおり、XmlSerializerのデフォルトのコンストラクターを使用するだけでなく、属性をインスタンス化するコンストラクターを作成しXmlAttributeOverridesて追加することができます。これを素晴らしくジェネリックにするために、名前空間を次の型からホバーします。型にその属性がない場合は失敗するため、このソリューションにはさらに作業が必要ですが、一般的な考え方はわかります。XmlAttributesXmlRoot((XmlTypeAttribute)incomingType.GetCustomAttributes(typeof(XmlTypeAttribute), true)[0]).Namespace

よろしく、

J1M

于 2012-11-21T16:15:44.727 に答える