Wątek przeniesiony 2022-02-24 09:03 z Webmastering przez Ktos.

Utworzenie poprawnego modelu do serializacji

0

Siemanko wszystkim,

mam takie pytanie, mianowicie muszę wysłać Jsona requestem ale widziałem testowe dane i mam mały mętlik w głowie. W konkretnym miejscu Json wygląda tak:

{
  title: "test title",
  value: "test value"
},
{
  title: "test title 2",
  value: {
    title: "test title 3",
    value: "test value 3"
  }
}

Jak mam utworzyć model, który w jednym przypadku we właściwości value wstawi string, a w innym przypadku inny obiekt?

2

Nic nie piszesz o jezyku w ktorym chcesz to modelować. Poza tym to nie jest poprawny JSON.

0

@Saalin: racja, mój błąd, w C#. Zaraz ręcznie w systemie utworzę Claima i przekleję tu ten fragment Jsona który idzie do systemu

Json:

{
	"sections": [
		{
			"link": "/api/v3/...",
			"fields": [
				{
					"__self__": "/api/v3/.../email",
					"value": "test email",
					"title": "email"
				},
				{
					"__self__": "/api/v3/.../market",
					"value": "test market",
					"title": "market"
				},
				{
					"__self__": "/api/v3/.../products",
					"value": [
						{
							"link": "/api/v3/.../18336",
							"title": "some product"
						}
					],
					"title": "products"
				},
				(...)
			]
		},
		{
			"link": "/api/v3/...",
			"fields": [
				{
					"__self__": "/api/v3/...",
					"title": "Items"
				}
			]
		}
	]
}

pole products jest multiselectem

2

Jak C# to utworzyłobym klasę na zmapowanie obiektu tego typu co może być albo stringiem albo obiektem i dopisał własny TypeConverter, żeby go obsłużyć.

0

@Saalin: Mógłbyś mi trochę podpowiedzieć? Powiedzmy że tworzę klasę Field bazując na tym Jsonie, która wygląda mniej więcej tak:

public class Field
{
  public string ___self___ { get; set; }
  public string title { get; set; }
  public (?) value { get; set; }
}

jakiego typu ma być właściwość value?

2

Mogę podpowiedzieć, ale to intuicja/pamięć, bo nie mam teraz czasu, żeby to sprawdzać. Rozważałbym dwie możliwości:

  1. Either z jakiejś biblioteki lub coś w rodzaju Either na zamodelowanie tego typu (można to bardziej ucywilizować, chodzi o sens)
public class Field
{
    public string ___self___ { get; set; }
    public string title { get; set; }
    [TypeConverter(typeof(ValueConverter))]
    public Value value { get; set; }
}

public class Value
{
    public string StrVal { get; set; }
    public List<ValueField> ArrVal { get; set; }

    public bool IsString { get; }

    private Value(string s)
    {
        StrVal = s;
        IsString = true;
    }


    private Value(List<ValueField> arr)
    {
        ArrVal = arr;
        IsString = false;
    }
}

public class ValueField
{
    public string Link { get; set; }
    public string Title { get; set; }
}
  1. Interfejs
public class Field
{
    public string ___self___ { get; set; }
    public string title { get; set; }
    [TypeConverter(typeof(ValueConverter))]
    public IValue value { get; set; }
}

public interface IValue
{

}

public class StrVal : IValue
{
    public string Value { get; set; }
}


public class ArrVal : IValue
{
    public List<ValueField> Value { get; set; }
}

public class ValueField
{
    public string Link { get; set; }
    public string Title { get; set; }
}

W każdym z wypadków trzeba napisać implementację class ValueConverter : TypeConverter

0

@Saalin: Dzięki za rady, a czy dobrym pomysłem jest po prostu ustawienie typu object na value?

0

Używanie object jest w ogóle średnim pomysłem, nie wiem jak sobie wtedy poradzi Newtonsoft.Json jeśli z niego korzystasz, prawdopodobnie zostawi to co tam zastał czyli JValue, JArray lub JObject, trudno to nazwać deserializacją do modelu.

0

@Saalin: Póki co nie wiem czy będę to deserializował, póki co mowa była tylko o wysyłanie requestów, ale nie wiadomo jak to będzie w przyszłości więc chyba zdecyduje się na patent z interfejsem

0

Ja tu widzę co najmniej kilka opcji:

  1. Deserializacja do dynamic i sprawdzanie jakiego typu jest value value is string i value is Array itd
  2. Dokonać wstępnej deserializacji i sprawdzać pole __self__. Do tego celu możesz użyć obiektu pośredniego, który nie ma właściwości value zdefiniowanej, albo deserializować do JObject. W drugim etapie deserializować do odpowiedniego obiektu.
  3. To co napisał @Saalin, tylko ja bym użył JsonProperty i nie używał nazw właściwości typu __self__. Poza tym objąłbym interfejsem cały obiekt razem z title

Ja bym wybrał 2) lub 3). W obu przypadkach użyłbym klas implementujących jeden interfejs. Byłyby to prawdopodobnie klasy takie jak Products, Product, Email, ale to tylko na podstawie tego fragmentu, bo próbka jest zbyt mała, żebym powiedział na pewno.

0

@Sarrus: No tych pól tam będzie około 20

0

To może faktycznie lepiej to dzielić wg kontraktu, czyli string, tablica, liczba itp. To już Twoja decyzja.

0

@Saalin:

Saalin napisał(a):

(...)
W każdym z wypadków trzeba napisać implementację class ValueConverter : TypeConverter

Poszedłem za Twoją radą i próbuję zimplementować ten ValueConverter.
Jak mogę w tym konwerterze zwrócić listę ValueField'ów?

póki co mam tak:

public class ValueConverter : TypeConverter
{
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            return value.ToString();
        }
        else if (destinationType == typeof(List<ValueField>))
        {
            return new List<ValueField>()
            {

            };
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}
0

@pavarotti: wybacz, że wprowadziłem Cię w błąd, cały czas myślałem, że chodzi nam o operację w drugą stronę, tj. deserializację, mimo że jak byk jest w temacie, że chodzi o serializację... W tym wypadku to robota jest wręcz trywialna i oprócz interfejsów nic nam nie potrzeba, przykładowy kod:

public interface IB
{

}

public class A
{
    public IB Value { get; set; }
}

public class B1 : IB
{
    public string Value { get; set; }
}

public class B2 : IB
{
    public List<string> Value { get; set; }
}

var x = new List<A>
{
    new A
    {
        Value = new B1
        {
            Value = "Hello"
        }
    },
    new A
    {
        Value = new B2
        {
            Value = new List<string> { "Goodbye", "World" }
        }
    }
};

var result = JsonConvert.SerializeObject(x);

Wynik:

[
    {
        "Value": {
            "Value": "Hello"
        }
    },
    {
        "Value": {
            "Value": [
                "Goodbye",
                "World"
            ]
        }
    }
]

Natomiast w drugą stronę to już nie zadziała i przy var obj = JsonConvert.DeserializeObject<A>(result); rzuci wyjątkiem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1