1

I have a problem with the Names of my deserialized object, which I want to return.

At the moment I am writing a WebService, which requests data from another WebService and then returns this Data to an Application.

Example:

public class UserController
{
    public HttpResponseMessage GetUser()
    {
        // Get Data from WebService
        string jsonString = @"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', address{'field3': 'HelloWorld Ave.', 'field4': 'Somewhere'}}";

        // Make Object from JSON-String
        User user = JsonConvert.DeserializeObject<User>(jsonString);

        // Return Object to Application
        return Request.CreateResponse(HttpStatusCode.OK, user);
    }
}

public class User
{
    [JsonProperty("key")]
    public string Key { get; set; }

    [JsonProperty("field1")]
    public string FirstName { get; set; }

    [JsonProperty("field2")]
    public string LastName { get; set; }

    [JsonProperty("address")]
    public Address Address { get; set; }
}

public class Address
{
    [JsonProperty("field3")]
    public string Street { get; set; }

    [JsonProperty("field4")]
    public string City { get; set; }
}

So far so good. My WebService creates the Object "User" and returns it to the Application.

And now my problem:

The returning JSON string changes the field names back to its original name.

Instead of:

"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"

I get:

"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"

I know that the PropertyName in JsonProperty is not Case-Sensitive, so I could write [JsonProperty("Key")] with an uppercase "K" and the returning Json-String will contain the Uppercase "K".

But what about my fields? Is there any way I can change "field1" to "FirstName", and "field2" to "LastName"?

Edit 1 - 2015-01-28: Added more code to the example.

3 Answers 3

1

I would suggest to perform 2 steps

  1. You (de)serialize your DTO (Data Transfer Object) as it is
  2. You implement an assembly layer where you control the creation of your domain objects.

In your case it seems that it's a 1:1 relation between DTO and DomainModel with just different property names - so you only need to project it that way. I can highly recommend Automapper Projections or in case it's nested Automapper Nested Projections for this.

The big advantage here is that you decouple your whole domain layer from your external services, so even if your webservices gets broken / changes, it doesn't affect your business logic.

Sign up to request clarification or add additional context in comments.

Comments

1

Both of @stefankmitph and @nhaberl answers were good answers, but I used a slightly different technic in the end.

Both of my classes now have private fields, for setting the value of the JSON-String to public-fields.

The private fields get the PropertyName of the incoming JSON-String and the public-fields get the PropertyName for the outgoing JSON-String:

public class User
{
    #region private

    [JsonProperty("key")]
    private string _Key { set { Key = value; } }

    [JsonProperty("field1")]
    private string _FirstName { set { FirstName = value; } }

    [JsonProperty("field2")]
    private string _LastName { set { LastName = value; } }

    [JsonProperty("address")]
    private Address _Address { set { Address = value; } }

    #endregion


    #region public

    [JsonProperty("Key")]
    public string Key { get; private set; }

    [JsonProperty("FirstName")]
    public string FirstName { get; private set; }

    [JsonProperty("LastName")]
    public string LastName { get; private set; }

    [JsonProperty("Address")]
    public Address Address { get; private set; }

    #endregion
}

public class Address
{
    #region private

    [JsonProperty("field3")]
    private string _Street { set { Key = value; } }

    [JsonProperty("field4")]
    private string _City { set { FirstName = value; } }

    #endregion


    #region public

    [JsonProperty("Street")]
    public string Street { get; private set; }

    [JsonProperty("City")]
    public string City { get; private set; }

    #endregion
}

Example output:

"{'Key': 'A45', 'FirstName': 'John', 'LastName': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"

With this method I could also get all Data from the unerlying classes to the main class:

public class User
{
    #region private

    // Other fields, see above. \\

    [JsonProperty("address")]
    private Address _Address 
    { 
        set 
        { 
            City = value.City;
            Street = value.Street;
        } 
    }

    #endregion


    #region public

    // Other fields, see above. \\

    [JsonProperty("City")]
    public string City { get; private set; }

    [JsonProperty("Street")]
    public string Street { get; private set; }

    #endregion
}

public class Address
{
    // Address fields. See above.
}

Example output:

"{'Key': 'A45', 'FirstName': 'John', 'LastName': 'Doe', 'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}"

1 Comment

You made that so much easier than it could have been. Thanks!
0

By writing your own serializer and adding a custom attribute to the properties you can force the serializer to take custom names when serializing back to Json.

public class JsonSerializeProperty : Attribute
{
    public string PropertyName { get; set; }
}

// derive from JsonConverter and override WriteJson (serialize) 
// and ReadJson (deserialize)
// note: i have not implemented CanConvert
public class CustomUserSerializer : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var user = value as User;
        if(user == null)
            throw new NullReferenceException("user");

        var properties = user.GetType().GetProperties();
        writer.WriteStartObject();
        foreach (var property in properties)
        {
            // get the attribute assigned to the property [JsonSerializeProperty]
            object customAttributes = property.GetCustomAttributes(typeof(JsonSerializeProperty), false).SingleOrDefault();
            JsonSerializeProperty attribute = customAttributes as JsonSerializeProperty;
            if(attribute != null)
            {
                // JsonSerializeProperty 
                string propertyName = attribute.PropertyName;

                // just write new property name and value
                writer.WritePropertyName(propertyName);
                writer.WriteValue(property.GetValue(value, new object[] {}));
            }
        }

        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // just map every JProperty from the Json string to the  
        // JsonProperty of the user class. I know this is kind of ugly... but it may serve your purpose
        JObject jsonObject = JObject.Load(reader);
        List<JProperty> properties = jsonObject.Properties().ToList();

        // create an instance of User to assign the values of the
        // Json string 
        object instance = Activator.CreateInstance(objectType);

        // loop through the user properties and get the 
        // JsonProperty customattribute. then set the value of the JProperty
        PropertyInfo[] objectProperties = objectType.GetProperties();
        foreach (var objectProperty in objectProperties)
        {
            var customAttribute = objectProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), false).SingleOrDefault();
            JsonPropertyAttribute attribute = customAttribute as JsonPropertyAttribute;
            if (attribute != null)
            {
                JProperty jsonProperty = properties.SingleOrDefault(prop => prop.Name == attribute.PropertyName);
                if (jsonProperty != null)
                {
                    objectProperty.SetValue(instance, jsonProperty.Value.ToString(), new object[] {});                        
                }
            }
        }

        return instance;
        // {
        //    _Key = "A45",
        //    _FirstName = "John",
        //    _LastName = "Doe"
        // }
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}

[JsonConverter(typeof(CustomUserSerializer))]
public class User
{
    [JsonProperty(PropertyName = "key")]
    [JsonSerializeProperty(PropertyName = "_Key")]
    public string Key { get; set; }

    [JsonProperty("field1")]
    [JsonSerializeProperty(PropertyName = "_FirstName")]
    public string FirstName { get; set; }

    [JsonProperty("field2")]
    [JsonSerializeProperty(PropertyName = "_LastName")]
    public string LastName { get; set; }
}

2 Comments

Hi @StephanKumpitsch, this works for my given example, but my real code is a little bit more complex (see above, I edited my example). Two questions. 1. What if I have another class in the JSON-String, like the current example? How do I get the values for that class? 2. This would only work for the class User, if I am not mistaken. What If I have more classes? Is there a way to rewrite your suggestion, so all classes can use the CustomSerializer?
@Lucas 1. You're right. It currently only works for 'flat' JSON-strings. 2. You could use it with any class that has the JsonConverter-Attribute (and JsonSerializeProperty-Attributes for its properties)... I'd suggest to go for a solution as suggested by nhaberl (this way there's no need for a custom JsonConverter and deserialization also works for nested classes).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.