Parsowanie json do odpowiedniego obiektu

0

Mam aplikacje która dostaje sparsowane obiekty do jsona, jak zrobić generyczną klasę parsująca json na odpowiedni obiekt?
Parsując json na klasę którą rozszerzają obiekty utracę dane które są zawarte w obiekcie roszerzajacym klase nadrzędną. Jak to zrobić prawidłowo i wydajnie?

0

Wklej kod z opisem bo 99,9% osób nie będzie się chciało wyobrażać hierarchii tego co tutaj napisałeś...

0

Zrób to za pomocą JAXB ;)

0

Ogarnołem to za pomocą json.org i refleksji:

 public static void initialize(JSONObject jsonObject, Object object) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		for(Field field : object.getClass().getDeclaredFields()) {
			String fieldSetter = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
			Class fieldClass = field.getType();
			Object fieldValue = null;
			
			try{
				fieldValue = jsonObject.get(field.getName());
			} catch (JSONException ex) {}
			
			Method method = object.getClass().getDeclaredMethod(fieldSetter, fieldClass);
			method.invoke(object, fieldValue);
		}
	}
2

A nie lepiej Jackson?

new ObjectMapper().readValue(jsonString, myObject.getClass());
0

Chodzi o to że dzięki dostępowi klucz wartość JSONObject mam od razu dostęp do pola które deklaruje typ wiadomości i do jakiego processora powinna być skierowana i dalej obrabiana (i wtedy dopiero wiadomo z jakim obiektem mam do czynienia), a żeby nie stosować kilku biebliotek do parsowania chciałem to rozwiązać jak mój przykład wyżej.

1

Jest sobie JSR-353 (i update w postaci JSR-374) i tam masz coś co się nazywa streaming API http://docs.oracle.com/javaee/7/tutorial/jsonp004.htm#BABDBHIA dziala dokładnie w ten sam sposób jak XML streaming (na poziomie idei) względnie modelem obiektowym można to potraktować http://docs.oracle.com/javaee/7/tutorial/jsonp003.htm#BABHAHIA

i nie trzeba żadnych udziwnień, bo "samo się mapuje" do obiektu. Jeżeli masz sytuację gdzie parsujesz klasę podrzędną, ale typem wyjściowym parsera jest klasa nadrzędna i nie chcesz utracić informacji, to musisz mieć jakiś kwalifikator w swoim jsonie, który określi z jakiego typu obiektem masz do czynienia.

0

Sytuacja wyglda tak:
Dostaje z zewnatrz json'a w stringu później parsuje go na klasę nadrzędną i mam wtedy dostęp do podstawowych pol w tym stringa z typem wiadomości wiec mogę go przesłać do odpowiedniego processora. Wtedy rzutowanie już na odpowiedni obiekt nie wchodzi w grę bo i tak dane się zgubia z klasy podrzędnej. Kolejne parsowanie stringa też kiepski pomysł jeśli chodzi o wydjaność.

0

O ile dobrze cię rozumiem to "tak to tylko w erze" niestety ;] Albo przy odbieraniu wiesz już co odbierasz i wtedy przez jaxb czy jacksona możesz sobie z tego złożyć obiekt, albo przy odbieraniu nie wiesz i wtedy będziesz parsował dwa razy. Albo sobie to odbierzesz jako mapę string->string

0

właśnie chodzi o to że nie wiadomo co przyjdzie :) dlatego żeby uniknąc 2 razy parsowania to używam json.org i wtedy mam dostęp do pól klucz-wartość wyciagajac wartość pola który informuje mnie o typie wiadomości mapuje sobie go ręcznie do odpowiedniego obiektu w ten sposób:

 public void process(String message) {
		JSONObject jsonObject = new JSONObject(message);
		String messageCategory = (String) jsonObject.get("messageCategory");
		String messageType = (String) jsonObject.get("messageType");
				
		if(messageCategory == null || !processorsByMessageCategory.containsKey(messageCategory)) {
			return;
		}
		
		Processor processor = processorsByMessageCategory.get(messageCategory);
		processor.process(jsonObject);
	}

Metoda w processorze:

public void process(JSONObject jsonObject) {
SomeMessage message = new SomeMessage();
MessageMapper.initializeObject( jsonObject, message); 
}

Metoda MessageMapper:

 public static void initializeObject(JSONObject jsonObject, Object object) throws NoSuchMethodException, SecurityException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
		for (Field field : object.getClass().getDeclaredFields()) {
			String fieldSetter = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
			Class fieldClass = field.getType();
			Object fieldValue = null;

			try {
				fieldValue = jsonObject.get(field.getName());
			} catch (JSONException ex) {
			}

			if (fieldValue instanceof JSONObject) {
				Object embededObject = fieldClass.newInstance();
				initializeObject((JSONObject) fieldValue, embededObject);
				fieldValue = embededObject;
			}

			Method method = object.getClass().getDeclaredMethod(fieldSetter, fieldClass);
			method.invoke(object, fieldValue);
		}
	}
0

o_O mam nadzieje że tam wyżej to żart. Po co piszesz rzeczy które juz istnieją? Jeszcze z pustymi catch i pewnie kilkoma corner case (typu obsługa kolekcji) które się tu wywalą.
Patrz np. jak zrobić to samo za pomocą JAXB https://examples.javacodegeeks.com/core-java/xml/bind/jaxb-json-example/
Jedyne co ci potrzebne to Class<T> dla klasy docelowej, ale skoro masz gdzieś ten docelowy typ zapisany jakoś to możesz zrobić Class.forName(...) od biedy albo choćby jakąś mapę string->class zmontować sobie i voila.

1

Wydaje mi się, że rozumiem autora i opiszę jego problem innymi słowami :)

Załóżmy, że po jednej stronie kanału komunikacyjnego mamy listę mebli, a więc List<Mebel>. Mebel może być krzesłem, stolikiem albo łóżkiem. Z tego względu nie mogę po drugiej stronie dekodować wszystkich elementów jednym parserem. Potrzebuję tyle parserów ile jest rodzajów mebli.

Na mój gust można rozwiązać problem wyboru parsera kompozycją zamiast na siłę pchać się w dziedziczenie. Konkretnie chodzi mi o to, że można zrobić parsowanie dwuetapowe. Najpierw parsujemy np do takiej klasy:

class OtagowanyMebel {
  String typMebla;
  String czystyJson;
}

Następnie na podstawie pola typMebla wybieramy konkretny parser z mapy parserów i parsujemy nim czystyJson.

Cały proces wygląda tak:

  • proces A ma w sobie List<Mebel>,
  • proces A konwertuje listę na List<OtagowanyMebel>
  • proces A wysyła tę listę otagowanych mebli do procesu B,
  • proces B odbiera listę List<OtagowanyMebel>
  • proces B konwertuje otrzymaną listę na List<Mebel>

Klasa OtagowanyMebel nie ma podtypów, więc nie ma problemu z jej deserializacją z JSONa. Problem przesuwa się w stronę konwersji między Mebel, a OtagowanyMebel, ale to powinno być proste i chyba nie muszę się nad tym rozwodzić.

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