Попытка сопоставить два разных типа с одним значением

У меня есть DataMember, который мне нужно заполнить строкой API JSON...

[DataContract]
public class Values
{
    [DataMember]
    public object value { get; set; }
}

JSON-строка API:

[
    {
        "type": "text",
        "values": [
            {
                "value": "Text that is in textfield"
            }
        ]
    },
    {
        "type": "category",
        "values": [
            {
                "value": {
                    "text": "Category title",
                    "color": "#000000"
                }
            }
        ]
    }
]

Я сопоставляю эту строку со строго типизированным объектом Field следующим образом:

    private List<Field> PrepFieldObject(string response)
    {
        using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(response)))
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Field>));
            return (List<Field>)serializer.ReadObject(stream);
        }
    }

но когда дело доходит до сопоставления Values.value, это вызывает шипение... Я пытался решить это следующим образом:

[DataContract]
public class Values
{
    [DataMember]
    public object value
    {
        get {
            return xamlValue;
        }
        set
        {                     
            xamlValue = new Value();

            if( value is string )
            {
                 xamlValue.text = (string)value; // This works fine
            }
            else
            {

                Value theValue = value as Value;
                try
                {
                    xamlValue.text = theValue.text; // Can't get hold of .text even though it does exist in the json.
                    xamlValue.color = theValue.color;
                }
                catch (Exception e)
                {
                }
            }
        }
    }
    public Value xamlValue { get; set; }
}

[DataContract]
public class Value
{
    [DataMember]
    public string text { get; set; }
    [DataMember]
    public string color { get; set; }
}

Но это не позволяет мне получить доступ к свойствам объекта (я думаю, потому что они никогда не отображались в DataContract)

я пытался добавить

[KnownType(typeof(Value))]
[KnownType(typeof(string))]

но и это не помогает :'(


person Jimmyt1988    schedule 11.06.2014    source источник
comment
Вам следует избегать ситуации, когда вы не знаете, будет ли объект строкой или сложным объектом с некоторым строковым свойством, которое вам нужно извлечь. Решения будут зависеть от контекста, который не показан, но, возможно, вам следует извлечь строковое значение из сложного объекта, прежде чем сохранять его в своем свойстве value.   -  person Servy    schedule 12.06.2014
comment
В таком случае, как вы думаете, вы могли бы помочь мне с геттером и сеттером? если заданное значение не имеет строкового типа, то вместо этого использовать объект? Я буду бороться, если вы не можете найти время. Спасибо хоть.   -  person Jimmyt1988    schedule 12.06.2014
comment
Вы не должны пытаться установить сложный тип в первую очередь. Какой бы код у вас ни вызывал этот установщик и присваивал сложный тип, следует изменить, чтобы вместо этого присвоить строковое значение этого сложного типа. Не зная, что это за код, я не мог сказать, что именно нужно изменить.   -  person Servy    schedule 12.06.2014
comment
Это API, которое возвращает строку json. Я использую поток вместе с newtonsoft json для сопоставления с классами строгого типа. к сожалению, на этом конкретном [DataMember] он отображается по-разному, поэтому мне нужен умный способ сопоставить его с сильным в зависимости от того, что это такое, или просто сделать это динамическим (но это не работает с xaml, кажется)   -  person Jimmyt1988    schedule 12.06.2014
comment
извините, я имею в виду DataContractJsonSerializer   -  person Jimmyt1988    schedule 12.06.2014


Ответы (6)


Вы должны привести его к любому типу, к которому принадлежит свойство text.

((SomeType)value).text
person Andrew Arnold    schedule 11.06.2014

Вы можете просто использовать оператор is, но вы должны использовать его для каждого значения, которое может принять value:

if (value is String)
{
    //do something
}
else if (value is ...)
...

Вы можете прочитать больше о is здесь.

person Marco Speziali    schedule 11.06.2014
comment
Это предполагает, что он знает все возможные типы во время компиляции; это, кажется, не так. - person Servy; 12.06.2014
comment
Он сказал, что это может быть строка или объект из API. Даже если это из API, он должен знать хотя бы общедоступные типы. - person Marco Speziali; 12.06.2014
comment
Во время выполнения, да, во время компиляции, не обязательно. Он может создавать тип, который будет использоваться другими типами, которых еще даже не существует, если в других проектах в конечном итоге будет использоваться код, который он пишет. И если на то пошло, даже если это вариант, который может быть или не быть, это все равно действительно плохая практика. намного лучшее решение — не ставить себя в такое положение с самого начала, как упоминается в моем комментарии выше, или, по крайней мере, использовать полиморфизм с помощью, скажем, интерфейса. - person Servy; 12.06.2014

Вы МОЖЕТЕ использовать динамический тип:

DataContract]
public class Values
{
    [DataMember]
    public dynamic value { get; set; }
}

Подробнее о Dynamics

person RyanTimmons91    schedule 11.06.2014
comment
В этом случае я предполагаю, что xaml должен иметь строгий тип, чтобы что-то отображать :( - person Jimmyt1988; 12.06.2014
comment
Это предполагает, что он знает, какое свойство объекта ему нужно получить, но он не обязательно знает, в этом проблема. - person Servy; 12.06.2014
comment
Ах, ну в таком случае кастинг может быть вашим единственным вариантом - person RyanTimmons91; 12.06.2014
comment
На самом деле я знаю... только потому, что в родительском классе есть свойство с именем type, которое я включаю для представления с помощью моего селектора шаблонов данных. когда это тип текста, значение будет строкой. когда это категория типа, свойство значения должно быть сопоставлено с объектом с различными другими свойствами... и поскольку я хочу использовать библиотеку json newtonsoft и поток, мне трудно выяснить, в какой момент я могу решить что отображать как - person Jimmyt1988; 12.06.2014

Если вам нужна строка или определенное строковое представление объекта, используйте:

String valueAsString = value.ToString()

Это предполагает, что другие типы (помимо строки) переопределяют метод ToString(), чтобы обеспечить осмысленное строковое представление объекта.

В противном случае вам нужно будет использовать:

KnownValueType kvt = value as KnownValueType
if(kvt!=null)
{
  //access known type properties here
}
person RJ Programmer    schedule 11.06.2014

Вам придется использовать приведение типов. Пример строки:

string abc = (string)value.text;

ИЛИ (желательно)

ComplexType comp = value as ComplexType;

ОБНОВЛЕНИЕ:

Когда вы пытаетесь сериализовать объект JSON и преобразовать его в строго типизированный объект (примером чего является мой ComplexType), важно иметь те же имена для свойств, что и в JSON. После сериализации вы должны получить доступ к значениям свойств.

Надеюсь это поможет!!!

person Satwik Nadkarny    schedule 11.06.2014
comment
Что такое бит ComplexType? - person Jimmyt1988; 12.06.2014
comment
ComplexType был просто примером типа, который вы должны были создать, например, класс Person или класс Student. Если вы пытались привести тип к такому типу, вы можете использовать 'as' для безопасного приведения типов. - person Satwik Nadkarny; 12.06.2014
comment
Ваше дополнительное редактирование, к сожалению, не помогает. Сериализация работает нормально, за исключением случаев, когда ее необходимо сопоставить с динамическим/объектным типом. - person Jimmyt1988; 12.06.2014

Я думаю, вы можете использовать отражение для доступа к значению свойства вашего объекта. Попробуйте изменить часть else вашего сеттера как

else
    {

        Value theValue = value as Value;
        try
        {   
            // pass the object and the property name you're trying to get hold of
            xamlValue.text = PropertyHasValue(theValue, "text"); // Can't get hold of .text even though it does exist in the json. 
            xamlValue.color = PropertyHasValue(theValue, "color");
        }
        catch (Exception e)
        {
        }
    }

И метод отражения

// using reflection to get the object's property value
public static String PropertyHasValue(object obj, string propertyName)
{
    try
    {
        if (obj != null)
        {
            PropertyInfo prop = obj.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
            if (prop != null)
            {
                string sVal = String.Empty;
                object val = prop.GetValue(obj, null);

                if (prop.PropertyType != typeof(System.DateTime?))
                    sVal = Convert.ToString(val);
                else // format the date to contain only the date portion of it
                    sVal = Convert.ToDateTime(val).Date.ToString("d"); ;

                if (sVal != null)
                {
                    return sVal;
                 }
             }
        }

        return null;
    }
    catch
    {
        return null;
    }
}
person Dennis R    schedule 11.06.2014
comment
Он перестал выдавать ошибки, что является шагом вперед (я думаю), но он изо всех сил пытается получить значения из объекта (он просто устанавливает эти свойства как нулевые). Я думаю, потому что он никогда не был сериализован из строки json? - person Jimmyt1988; 12.06.2014
comment
Фактически, параметр объекта PropertyHasValue имеет значение null. - person Jimmyt1988; 12.06.2014
comment
Если объект, который вы передаете, имеет значение («текст и цвет»), этот метод обязательно должен вернуть значение. Проверьте, если что-то еще идет не так. - person Dennis R; 12.06.2014
comment
Кажется, что DataContractJsonSerializer работает для всего остального, кроме этого свойства двойного типа. Дело в том, что DataContractJsonSerializer просто не справляется с типом объекта? хотя кажется, что строка отлично сопоставляется! - person Jimmyt1988; 12.06.2014
comment
Я так думаю, он может просто хорошо отображать строку, но не уверен, как DataContractJsonSerializer обрабатывает сопоставление объектов. - person Dennis R; 12.06.2014