@@ -285,11 +285,101 @@ protected void marshallField(final MarshallingContext context, Object newObj, Fi
285285
286286 @ Override
287287 public Object unmarshal (final HierarchicalStreamReader reader , final UnmarshallingContext context ) {
288+ String readResolveValue = reader .getAttribute (mapper .aliasForAttribute ("resolves-to" ));
289+ Class type = readResolveValue != null ? mapper .realClass (readResolveValue ) : context .getRequiredType ();
290+ if (type != null && type .isRecord ()) {
291+ return unmarshalRecord (reader , context , type );
292+ }
293+
288294 Object result = instantiateNewInstance (reader , context );
289295 result = doUnmarshal (result , reader , context );
290296 return serializationMethodInvoker .callReadResolve (result );
291297 }
292298
299+ private Object unmarshalRecord (final HierarchicalStreamReader reader , final UnmarshallingContext context , Class type ) {
300+ Map <String , Object > values = new HashMap <>();
301+
302+ Iterator it = reader .getAttributeNames ();
303+ while (it .hasNext ()) {
304+ String attrAlias = (String ) it .next ();
305+ String attrName = mapper .attributeForAlias (attrAlias );
306+ Field field = reflectionProvider .getFieldOrNull (type , attrName );
307+ if (field != null ) {
308+ SingleValueConverter converter = mapper .getConverterFromAttribute (field .getDeclaringClass (), attrName , field .getType ());
309+ Class fieldType = field .getType ();
310+ if (converter == null ) {
311+ converter = mapper .getConverterFromItemType (fieldType );
312+ }
313+ if (converter != null ) {
314+ Object value = converter .fromString (reader .getAttribute (attrAlias ));
315+ if (fieldType .isPrimitive ()) {
316+ fieldType = Primitives .box (fieldType );
317+ }
318+ if (value != null && !fieldType .isAssignableFrom (value .getClass ())) {
319+ throw new ConversionException ("Cannot convert type " + value .getClass ().getName () + " to type " + fieldType .getName ());
320+ }
321+ values .put (attrName , value );
322+ }
323+ }
324+ }
325+
326+ while (reader .hasMoreChildren ()) {
327+ reader .moveDown ();
328+ try {
329+ String fieldName = mapper .realMember (type , reader .getNodeName ());
330+ Field field = reflectionProvider .getFieldOrNull (type , fieldName );
331+ if (field != null ) {
332+ Class fieldType = field .getType ();
333+ Class xmlType = mapper .defaultImplementationOf (fieldType );
334+ String classAttribute = reader .getAttribute (mapper .aliasForAttribute ("class" ));
335+ if (classAttribute != null ) {
336+ Class specifiedType = mapper .realClass (classAttribute );
337+ if (fieldType .isAssignableFrom (specifiedType )) {
338+ xmlType = specifiedType ;
339+ }
340+ }
341+ Object value = unmarshalField (context , null , xmlType , field );
342+ if (value != null && !xmlType .isAssignableFrom (value .getClass ())) {
343+ LOGGER .warning ("Cannot convert type " + value .getClass ().getName () + " to type " + xmlType .getName ());
344+ } else {
345+ values .put (fieldName , value );
346+ }
347+ } else {
348+ Class itemType = mapper .getItemTypeForItemFieldName (type , fieldName );
349+ Class xmlType = itemType != null ? itemType : mapper .realClass (reader .getNodeName ());
350+ context .convertAnother (null , xmlType );
351+ }
352+ } catch (CriticalXStreamException | InputManipulationException e ) {
353+ throw e ;
354+ } catch (XStreamException | LinkageError e ) {
355+ addErrorInContext (context , e );
356+ }
357+ reader .moveUp ();
358+ }
359+
360+ try {
361+ java .lang .reflect .RecordComponent [] components = type .getRecordComponents ();
362+ Class [] parameterTypes = new Class [components .length ];
363+ Object [] args = new Object [components .length ];
364+ for (int i = 0 ; i < components .length ; i ++) {
365+ java .lang .reflect .RecordComponent component = components [i ];
366+ Class pType = component .getType ();
367+ String name = component .getName ();
368+ parameterTypes [i ] = pType ;
369+ Object val = values .get (name );
370+ if (val == null && pType .isPrimitive ()) {
371+ val = java .lang .reflect .Array .get (java .lang .reflect .Array .newInstance (pType , 1 ), 0 );
372+ }
373+ args [i ] = val ;
374+ }
375+ java .lang .reflect .Constructor constructor = type .getDeclaredConstructor (parameterTypes );
376+ constructor .setAccessible (true );
377+ return constructor .newInstance (args );
378+ } catch (Exception e ) {
379+ throw new ConversionException ("Failed to instantiate record " + type .getName (), e );
380+ }
381+ }
382+
293383 public Object doUnmarshal (final Object result , final HierarchicalStreamReader reader , final UnmarshallingContext context ) {
294384 final SeenFields seenFields = new SeenFields ();
295385 Iterator it = reader .getAttributeNames ();
0 commit comments