JAX-RS | allgemeiner MessageBodyWriter für List<?>

Hallo,

ich versuche mich aktuell mit JAX-RS auf einem WildFly8 auseinander zu setzen.
Jetzt wollte ich einen REST-Endpoint erstellen, welcher verschiedene ArrayList-Objekte mit unterschiedlichen generischen Typen bereit stellen kann.

Ich bin aktuell soweit, das ich einen eigenen MessageBodyWriter erstellen muss. Für einzelen spezielle generische Typen klappt das auch alles wunderbar. Aber mir schwebt eine zentralle MessageBodyWriter Klasse vor, welche alle List-Objekte egal mit welchem generischen Typ verarbeiten kann.

@Provider
@Produces("application/xml")
public class Test_MessageBodyWriter implements MessageBodyWriter<List<?>> {
	@Context
	protected Providers providers;

...

	@Override
	public void writeTo(List<?> list, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
			MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
		try{
			Map<String,String> h = mediaType.getParameters();
			Charset c = Charset.forName(h.get(MediaType.CHARSET_PARAMETER));
			String cName = c.name();
			
			
			JAXBContext ctx = null;
			ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, mediaType);
			
			if(resolver != null){
				ctx = resolver.getContext(type);
			}
			if(ctx == null){
				ctx = JAXBContext.newInstance(type);
			}
			Marshaller m = ctx.createMarshaller();
			
			/*
			JAXBContext context = JAXBContext.newInstance(type);
			Marshaller m = context.createMarshaller();
			*/
			
		    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		    m.setProperty(Marshaller.JAXB_FRAGMENT, true);
			
			entityStream.write(String.format("<?xml version=\"1.0\" encoding=\"%s\" standalone=\"yes\"?>", cName).getBytes(cName));
			entityStream.write(String.format("<%s>", "list").getBytes(cName));
			
			if(list.size() > 0){
				JAXBContext.newInstance(list.get(0).getClass());
				for (Object o : list){
					m.marshal(o, entityStream);
				}
			}

			entityStream.write(String.format("</%s>", "list").getBytes(cName));

		} catch(Exception e){
			e.printStackTrace();
		}
	}```

Leider funktioniert mein Ansatz nicht wirklich. Ich bekomme folgende Fehlermeldung.
`javax.xml.bind.JAXBException: Weder class de.test.TestClass noch eine der zugehörigen Superklassen ist diesem Kontext bekannt.`

Kann mir bitte jemand einen Tipp geben, worauf ich achten muss, um mein Vorhaben umzusetzen?

Grüße Hans

Leider habe ich keine Ahnung von JAXB, aber … vielleicht hilft ja schon die spezielle Form, in der ich jetzt die Vermutung äußere, dass du schon nach der Englischen Version der Fehlermeldung gesucht und sowas wie java - Nor any of its super class is known to this context - Stack Overflow gefunden hast, und das nicht geholfen hat?

Darf ich fragen, welchen JAX-RS / JAX-B Provider du nutzt? In neueren Varianten ist der MessageBodyWriter teilweise nicht mehr erforderlich, z.B. mit Jackson.

Mir war es bisher nicht möglich, einen generischen MessageBodyWriter für alle Listen zu schreiben. Soweit ich es verstehe, hat JAX-RS zur Laufzeit keine Möglichkeit zu bestimmen, welche Art der Liste es gerade behandelt, Stichwort Type Erasure.

Eine Möglichkeit diesem Problem beizukommen ist die Generic Entity: JAX-RS: Returning A List Of Instances, Problem and Solution : Adam Bien’s Weblog

Hallo,

also über die Target-Runtime wird mir für JAX-RS und JAX-B die Version “3.0.8.Final” eingebunden.

Mit dem Ansatz des GenericEntity konnte ich auch nicht wirklich eine vernünftige Umsetzung erzielen.

Ich konnte zwar schon mal einen Fehler beheben, aber jetzt heißt das Root-Element für alle Listen immer “list”.

	public void writeTo(ArrayList<?> list, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
			MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
		try{
			Map<String,String> h = mediaType.getParameters();
			Charset c = Charset.forName(h.get(MediaType.CHARSET_PARAMETER));
			String cName = c.name();

			entityStream.write(String.format("<?xml version=\"1.0\" encoding=\"%s\" standalone=\"yes\"?>", cName).getBytes(cName));
			entityStream.write(String.format("<%s>", "list").getBytes(cName));
			
			if(list.size() > 0){
				JAXBContext context = JAXBContext.newInstance(list.get(0).getClass());
				Marshaller m = context.createMarshaller();
				m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			    m.setProperty(Marshaller.JAXB_FRAGMENT, true);
				
				for (Object o : list){
					m.marshal(o, entityStream);
				}
			}

			entityStream.write(String.format("</%s>", "list").getBytes(cName));

		} catch(Exception e){
			e.printStackTrace();
		}
	}```

Das geht soweit auch ersteinmal, aber zukünftig wäre es besser, wenn das Root-Element in Anlenung an das eigentliche Objekt benannt wird.