Skip to content
Advertisement

Java Streams return throws error Unexpected return value

I am quite new to Java streams and I am trying to implement a method using the streams but when I try to return the value it’s throwing me the error Unexpected return value. I just wanted to know what am I doing wrong.

Following is the return within the Arrays.stream value which is throwing the error:

Object[] result = extensionBuilder((Map) extension.getValue());
Arrays.stream(result).forEach(innerExtension -> {
    return new JAXBElement<JAXBElement>(new QName(getCleanLabel(extension.getKey())), JAXBElement.class, (JAXBElement) innerExtension);
});

However, if I try to return a single value outside the Arrays.stream then I am able to get the first value. (As you can see I am returning the first value only result[0])

Object[] result = extensionBuilder((Map) extension.getValue());
Arrays.stream(result).forEach(innerExtension -> {
    System.out.println((JAXBElement) innerExtension);
});
return new JAXBElement<JAXBElement>(new QName(getCleanLabel(extension.getKey())), JAXBElement.class, (JAXBElement) result[0]);

I want to pass the values from the Object[] result one by one and return the result to my calling method.

Here is the complete method that I have:

  private Object[] extensionBuilder(Map<String, Object> extensions) {
    if (extensions == null) {
      return null;
    }
    return extensions.entrySet().stream().map(extension -> {
      if (extension.getValue() instanceof Map) {

        // return new JAXBElement<JAXBElement>(new QName(getCleanLabel(extension.getKey())), JAXBElement.class,
        //    (JAXBElement) extensionBuilder((Map) extension.getValue())[0]);

        Object[] result = extensionBuilder((Map) extension.getValue());
        Arrays.stream(result).forEach(innerExtension -> {
          System.out.println((JAXBElement) innerExtension);
        });
        return new JAXBElement<JAXBElement>(new QName(getCleanLabel(extension.getKey())), JAXBElement.class, (JAXBElement) result[0]);

      } else {
        return new JAXBElement<String>(new QName(getCleanLabel(extension.getKey())), String.class, extension.getValue().toString());
      }
    }).filter(Objects::nonNull).collect(Collectors.toList()).toArray(Object[]::new);
  }

Here is the input given to the method:

{nameSpace:MyField-2={fb:My-Sub-Field-1={nameSpace:MyField-1=myValue, nameSpace:MyField-2=myValue, nameSpace:MyField-3=myValue}, nameSpace2:My-Sub-Field-2={nameSpace:MyField-4=myValue4, nameSpace:MyField-5=myValue5, nameSpace:MyField-6=myValue6}, nameSpace:MyField-7MyField-3=myValue1}, nameSpace:MyField-8=Name}

I am a bit confused about this for a long time now. Can someone please assist me with how to accomplish the returning concept using the stream?

Basically, I would like to return the value for every element within the Object[] result as of now I am able to do it only for one element but I want for each object present within the Object[] result

***** EDITED ON 11 May ******

Based on the response to the question I am able to do it but the response I am getting looks something like this:

<namespace:MainField>
    <namespace:SubField1>
        <namespace:Field1>Value1</namespace:Field1>
    </namespace:SubField1>
</namespace:MainField>
<namespace:MainField>
    <insta:SubField2>
        <insta:Field2>Value2</insta:Field2>
    </insta:SubField2>
</namespace:MainField>
<namespace:MainField>
    <namespace:SubField3>SubValue3</namespace:SubField3>
</namespace:MainField>
<namespace:MainField2>MainValue2</namespace:MainField2>
<namespace:MainField3>MainValue3</namespace:MainField3>

Whereas I am looking for something like this:

<namespace:MainField>
    <namespace:SubField1>
        <namespace:Field1>Value1</namespace:Field1>
    </namespace:SubField1>
    <insta:SubField2>
        <insta:Field2>Value2</insta:Field2>
    </insta:SubField2>
    <namespace:SubField3>SubValue3</namespace:SubField3>
</namespace:MainField>
<namespace:MainField2>MainValue2</namespace:MainField2>
<namespace:MainField3>MainValue3</namespace:MainField3>

This is due the stream which is returning the JAXBElement for every element within the Object[] result I would like to return the result after completing the looping the child stream something like this:

  private Object[] extensionBuilder(Map<String, Object> extensions) {
    if (extensions == null) {
      return null;
    }
    return extensions.entrySet().stream().flatMap(extension -> {
      if (extension.getValue() instanceof Map) {
        // return Stream of result transformed to JAXBElements
        return Arrays.stream(extensionBuilder((Map) extension.getValue())).map(innerExtension -> {
          System.out.println(innerExtension);
          return new JAXBElement<JAXBElement>(new QName(getCleanLabel(extension.getKey())), JAXBElement.class, (JAXBElement) innerExtension);
        });
      } else {
        return Stream.of(new JAXBElement<String>(new QName(getCleanLabel(extension.getKey())), String.class, extension.getValue().toString()));
      }
    }).filter(Objects::nonNull).collect(Collectors.toList()).toArray();
  }

I tried this but this is throwing me the error java.lang.ClassCastException: class java.util.ArrayList cannot be cast to class javax.xml.bind.JAXBElement.

Can someone please assist me on how to modify the Java Stream so that I can have the expected output?

Advertisement

Answer

You’re trying to return some value from lambda passed to forEach. That lambda type is Consumer<T> and its method accept return type is void so you can’t return any value from it.

If you want to return collection of objects from you method, I’d suggest to rewrite it to return Stream:

  private Object[] extensionBuilder(Map<String, Object> extensions) {
    if (extensions == null) {
      return null;
    }
    //                        use flatMap vvvvvvv instead of map
    return extensions.entrySet().stream().flatMap(extension -> {
      if (extension.getValue() instanceof Map) {
        Object[] result = extensionBuilder((Map) extension.getValue());
        // return Stream of result transformed to JAXBElements
        return Arrays.stream(result).map(innerExtension -> {
          return new JAXBElement<JAXBElement>(new QName(getCleanLabel(extension.getKey())), JAXBElement.class, (JAXBElement) result[0]);
        });
      } else {
        // return Stream of single item
        return Stream.of(
          new JAXBElement<String>(new QName(getCleanLabel(extension.getKey())), String.class, extension.getValue().toString())
        );
      }
    }).filter(Objects::nonNull).collect(Collectors.toList()).toArray();
  }

See Stream::flatMap docs.


You can deal without result variable if you just inline it:

  private Object[] extensionBuilder(Map<String, Object> extensions) {
    if (extensions == null) {
      return null;
    }

    return extensions.entrySet().stream().flatMap(extension -> {
      if (extension.getValue() instanceof Map) {
        // return Stream of result transformed to JAXBElements
        return Arrays.stream(extensionBuilder((Map) extension.getValue()))
          .map(innerExtension -> new JAXBElement<JAXBElement>(
             new QName(getCleanLabel(extension.getKey())),
             JAXBElement.class,
             (JAXBElement) result[0]
          ));
      } else {
        return Stream.of(
          new JAXBElement<String>(new QName(getCleanLabel(extension.getKey())), String.class, extension.getValue().toString())
        );
      }
    }).filter(Objects::nonNull).collect(Collectors.toList()).toArray();
  }

But be careful it’s better to come up with a better name for this variable than just inlining it, because it lacks readability (IMHO).

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement