Get nullable variable Serialized datetime in WCF service responses

Asked

Viewed 195 times

0

I have a WCF service with a variable nullable Datetime in a Datacontract as shown below. Because of business rules this Datamember may not have the Emitdefaultvalue setado to true and the guy has to be "Datetime?"

[DataContract (Name = "DADOS")]
classe public Dados
{
    [DataMember (EmitDefaultValue = false, Name = "NASCIMENTO")]
    public DateTime? DtNascimento = null;
}

Man Datacontract is specified as below, see that I have to have two versions of the method Webinvoke to maintain interoperability between different systems (responses in JSON and XML format):

    [ServiceContract]
    public interface IRestService
    {
        [OperationContract (Name = "ConsultaDadosXml")]
        [WebInvoke(Method = "GET", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "ConsultaDados/xml?token={token}")]
        Dados ConsultaDadosXml (token string);

        [OperationContract (Name = "ConsultaDadosJson")]
        [WebInvoke(Method = "GET", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "ConsultaDados/json?token={token}")]
        Dados ConsultaDadosJson (token string);
    }

The problem is that when Dtnascimento comes correctly populated with a good valid database value, everything works fine, when it comes with a null database value, the XML/JSON response comes without the BIRTH tag, ok, this is happening because Emitdefaultvalue = false. I can define, through procedures that my database send me an empty value, only when I do this the serialized object comes with a Mindate value in the answers.

Xml version:

     <DADOS>
         <NASCIMENTO> 1900-01-01T00: 00: 00 </ NASCIMENTO>
     </ DADOS>

Json version:

{
    "NASCIMENTO": "/ Date (-2208981600000-0200) /",
}

What I really need is an empty variable(tag) being shown in the answers when this value is null, this is because there are other connected systems in the web service trying to interpret these values, so the best solution would be to keep these variables empty as follows:

Xml:

<DADOS>
<NASCIMENTO> </ NASCIMENTO>
</ DADOS>

Json:

{
    "NASCIMENTO": "",
}

Could someone help me with some suggestion?

  • what happens when you put the IsRequired = true next to the EmitDefaultValue = false and sends a null? I believe that’s what you’re looking for.

  • @Andrade, I started receiving a System.ServiceModel.Communicationexception. I think it’s because I’m getting zero comic book value and marking him as Required in my contract. When BD responds with a non-zero value, Exception does not occur.

1 answer

0


The first idea to solve this problem would be to use an extra property, like string, decorated with the [DataMember] as in the code below:

[DataContract(Name = "DADOS", Namespace = "")]
public class Dados
{
    public DateTime? DtNascimento = null;
    [DataMember(EmitDefaultValue = false, Name = "NASCIMENTO")]
    private string WireDtNascimento
    {
        set { /* nao usado */ }
        get
        {
            return this.DtNascimento == null ?
                "" :
                DtNascimento.Value.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK");
        }
    }
}

The problem is that as you need to use the same type in two different serialization forms, and the format of the DateTime in XML (the one I used in the example above) is different from the JSON format (\/Date(...)\/).

There are two solutions to solve this problem: you can look at the call stack to see if the serializer being used is JSON, and use this information to decide how you will convert the value. It works, but it’s a hack quite large (you are basing your logic according to a WCF implementation detail) that I would not advise...

[DataContract(Name = "DADOS", Namespace = "")]
public class Dados
{
    public DateTime? DtNascimento = null;
    [DataMember(EmitDefaultValue = false, Name = "NASCIMENTO")]
    private string WireDtNascimento
    {
        set { /* nao usado */ }
        get
        {
            if (this.DtNascimento == null)
            {
                return "";
            }
            else
            {
                var usandoJson = new StackTrace().GetFrames().Any(st => st.GetMethod().DeclaringType == typeof(DataContractJsonSerializer));
                if (usandoJson)
                {
                    var millis = (long)this.DtNascimento.Value.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
                    var offset = this.DtNascimento.Value.Kind == DateTimeKind.Utc ? "" : this.DtNascimento.Value.ToString("%K").Replace(":", "");
                    return "/Date(" + millis + offset + ")/";
                }
                else
                {
                    return this.DtNascimento.Value.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK");
                }
            }
        }
    }
}

Another alternative - since you are using two methods, you can use one DataContract for each response format. There will be a little more code duplication, but you win in readability.

[DataContract(Name = "DADOS", Namespace = "")]
public class DadosJson
{
    public DateTime? DtNascimento = null;
    [DataMember(EmitDefaultValue = false, Name = "NASCIMENTO")]
    private string WireDtNascimento
    {
        set { /* nao usado */ }
        get
        {
            if (this.DtNascimento == null)
            {
                return "";
            }
            else
            {
                var millis = (long)this.DtNascimento.Value.ToUniversalTime()
                    .Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))
                    .TotalMilliseconds;
                var offset = this.DtNascimento.Value.Kind == DateTimeKind.Utc ?
                    "" :
                    this.DtNascimento.Value.ToString("%K").Replace(":", "");
                return "/Date(" + millis + offset + ")/";
            }
        }
    }
}
[DataContract(Name = "DADOS", Namespace = "")]
public class DadosXml
{
    public DateTime? DtNascimento = null;
    [DataMember(EmitDefaultValue = false, Name = "NASCIMENTO")]
    private string WireDtNascimento
    {
        set { /* nao usado */ }
        get
        {
            return this.DtNascimento == null ?
                "" :
                this.DtNascimento.Value.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK");
        }
    }
}
[ServiceContract]
public interface IRestService
{
    [OperationContract(Name = "ConsultaDadosXml")]
    [WebGet(ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "ConsultaDados/xml?token={token}")]
    DadosXml ConsultaDadosXml(string token);

    [OperationContract(Name = "ConsultaDadosJson")]
    [WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "ConsultaDados/json?token={token}")]
    DadosJson ConsultaDadosJson(string token);
}
public class Service : IRestService
{
    public DadosXml ConsultaDadosXml(string token)
    {
        var result = new DadosXml();
        if (!"NULL".Equals(token, StringComparison.OrdinalIgnoreCase))
        {
            result.DtNascimento = DateTime.Now;
        }

        return result;
    }

    public DadosJson ConsultaDadosJson(string token)
    {
        return new DadosJson
        {
            DtNascimento =  ConsultaDadosXml(token).DtNascimento
        };
    }
}
  • Realment I have already thought about the solution you present with good readability, the problem is that the system is large and there are customers already using the service, I can not change the contract. (return of Data => Dataxml/Dadosjson). Would the hack solution presented generate some kind of vulnerability in the service? I had thought of something similar, but not about getting the information from Stacktrace but from the URI because in Webinvoke I declare: Uritemplate = "Queried/json? token={token}"

  • Good idea, if at the time of creating the class Dados you pass the kind of serialization that might work. This solution (using another property in serialization) will not generate any vulnerability, I would say that this is the recommended solution for this scenario.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.