2016-06-29 5 views
6

У меня есть следующие JS строка:Json.NET десериализации или сериализации JSON строки и карт свойств различных имен свойств, определенных во время выполнения

{ 
    "values": { 
     "details": { 
      "property1": "94", 
      "property2": "47", 
      "property3": "32", 
      "property4": 1 
     }, 
     count: 4 
    } 
}  

Я иду на карту это к следующей модели:

public class Details 
{ 
    public string property1 { get; set; } 
    public string property2 { get; set; } 
    public string property3 { get; set; } 
    public int property4 { get; set; } 
} 

public class Values 
{ 
    public Details details { get; set; } 
    public int count { get; set; } 
} 

public class RootObject 
{ 
    public Values values { get; set; } 
} 

Я хочу, чтобы иметь возможность сопоставить эти имена свойств для различных имен во время выполнения, когда десериализаций этого JSON строка, как это:

JsonConvert.DeserializeObject<RootObject>(jsonString); 

Например, в процессе десериализации я хочу десериализовать имя «property1» в «differen_property_name1» или «differen_property_name2» или «differen_property_name3». Поскольку я выбираю новое имя во время выполнения (новое название, к которому я изменю название «property1» к), я не могу использовать решение с использованием JsonPropertyAttribute, как предложено здесь:

.NET NewtonSoft JSON deserialize map to a different property name

Один из ответов на этот вопрос (ответ Джека) использует наследование DefaultContractResolver, но в этом случае он не работает.

Update

Позже, мне нужно сериализовать объект я получил от десериализации и сопоставления свойств различных имен свойств, определенных во время выполнения. Я использовал тот же метод, как Брайан предложил сделать сериализации:

Я использовал словарь, чтобы отобразить мои новые имена свойств:

var map = new Dictionary<Type, Dictionary<string, string>> 
{ 
    { 
     typeof(Details), 
     new Dictionary<string, string> 
     { 
      {"property1", "myNewPropertyName1"}, 
      {"property2", "myNewPropertyName2"}, 
      {"property3", "myNewPropertyName3"}, 
      {"property4", "myNewPropertyName4"} 
     } 
    } 
};  

, а затем я использовал DynamicMappingResolver Брайана сериализовать объект, как это:

var settings = new JsonSerializerSettings 
{ 
    ContractResolver = new DynamicMappingResolver(map) 
}; 

var root = JsonConvert.SerializeObject(myObjectInstance, settings);    
+0

Потому что я хочу, чтобы решить, во время выполнения которых новое имя, которое я хочу, чтобы дать собственность и JsonPropertyAttribute вы должны использовать «жесткий кодированный» имя. –

+0

Одним из способов (хотя это может быть некрасиво) было бы сначала десериализовать JSON, а затем сопоставить десериализованный объект с объектом с новыми именами. Однако может быть более элегантный способ. – Tim

+0

@Tim, как бы вы сопоставили десериализованный объект с объектом с новыми именами, когда новые имена должны определяться во время выполнения? –

ответ

5

Чтобы сделать это, вы можете использовать пользовательский ContractResolver. В основном это та же идея, что и атрибут [JsonProperty] для каждого члена класса, для которого вы хотите сопоставить другое имя свойства JSON, за исключением того, что вы делаете это программно через распознаватель. Вы можете передать словарь ваших желаемых сопоставлений распознавателю при настройке перед десериализацией.

Вот что пользовательский код распознаватель может выглядеть следующим образом:

class DynamicMappingResolver : DefaultContractResolver 
{ 
    private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap; 

    public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap) 
    { 
     this.memberNameToJsonNameMap = memberNameToJsonNameMap; 
    } 

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     JsonProperty prop = base.CreateProperty(member, memberSerialization); 
     Dictionary<string, string> dict; 
     string jsonName; 
     if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) && 
      dict.TryGetValue(member.Name, out jsonName)) 
     { 
      prop.PropertyName = jsonName; 
     } 
     return prop; 
    } 
} 

Чтобы использовать распознаватель, первый построить Dictionary<Type, Dictionary<string, string>>, содержащий ваши отображения. Ключ внешнего словаря - это тип (ы) класса, свойства которого вы хотите сопоставить; внутренний словарь - это сопоставление имен свойств класса с именами свойств JSON. Вам нужно только предоставить сопоставление свойств, имена которых еще не соответствуют JSON.

Так, например, если ваш JSON выглядит следующим образом (обратите внимание на измененные имена свойств внутри details объекта) ...

{ 
    "values": { 
     "details": { 
      "foo": "94", 
      "bar": "47", 
      "baz": "32", 
      "quux": 1 
     }, 
     count: 4 
    } 
} 

... и вы хотите, чтобы отобразить его на классы в вашем вопросе, вы можете создать словарь, как это:

var map = new Dictionary<Type, Dictionary<string, string>> 
{ 
    { 
     typeof(Details), 
     new Dictionary<string, string> 
     { 
      {"property1", "foo"}, 
      {"property2", "bar"}, 
      {"property3", "baz"}, 
      {"property4", "quux"} 
     } 
    } 
}; 

последний шаг, чтобы настроить параметры Serializer с новым экземпляром распознавателя, придав ему словарь отображение вы просто сконструированный, а затем передаст настройки JsonConvert.DeserializeObject().

var settings = new JsonSerializerSettings 
{ 
    ContractResolver = new DynamicMappingResolver(map) 
}; 

var root = JsonConvert.DeserializeObject<RootObject>(json, settings); 

Вот демо: https://dotnetfiddle.net/ULkB0J

+0

Именно то, что я искал. Благодаря!! –

0

Я не верю, что у JSON.net есть поддержка того, что вы ищете. Вы должны вместо этого десериализовать JSON в JObject, если вы не знаете формат или какой-то общий, если вы это делаете (например, если JSON всегда говорит property1, вы можете использовать общий объект для его представления).

Как только у вас есть общий объект, тогда вам нужно перевести поля. Любые, которые не могут быть изменены, могут выполняться напрямую, но для чего-либо еще вам понадобится использовать Reflection.

В основном это предполагает получение типа (typeof(Details) или obj.GetType()), а затем поиск недвижимости, которую вы хотите обновить. Наконец, вы должны найти способ установки и вызвать его исходное значение из вашего общего объекта.

1

Почему это за один шаг? Почему бы не десериализовать ваш стандартный объект и затем динамически их сопоставить с помощью Automapper?

что-то вроде:

Mapper.Initialize(c => 
{ 
    c.ReplaceMemberName("property1 ", "differen_property_name1"); 
}); 

 Смежные вопросы

  • Нет связанных вопросов^_^