2

I'm running into an issue I don't know how to resolve. I need to return a JSON object that has duplicate keys.

I understand this is perfectly valid according to the JSON specification. See ecma-404, page 6 that reads:

The JSON syntax does not impose any restrictions on the strings used as names, does not require that name strings be unique...

I need to return this:

{
  "id":"1401",
  "sku":"C18-25",
  "sku":"A15-70"
}

I've been using a typical rest method in Spring Boot:

@GetMapping("/{id}")
@ResponseBody
public ResponseEntity<?> getProduct(@PathVariable String id) {
  Map<String, String> r = new HashMap<>();
  r.put("id", "1401");
  r.put("sku", "C18-25");
  r.put("sku", "A15-70");
  return new ResponseEntity<Map<String, String>>(r, HttpStatus.OK);
}

But it only keeps the last entry:

{
  "id":"1401",
  "sku":"A15-70"
}

How can I return multiple entries with the same name?

12
  • 1
    Why would you need to do this? Commented Dec 21, 2022 at 14:50
  • 1
    The specs tell you that you that names within an object SHOULD be unique. SHOULD is not MUST, but it is highly recommended you follow that advice. It can lead to trouble with many tools. Why not make sku an array? Commented Dec 21, 2022 at 14:52
  • That’s non-sensical—any reasonable parsing would keep only the last value. At best you could hope for an object that converts keys with multiple values into a collection, but that’s problematic for the consuming code. Commented Dec 21, 2022 at 14:52
  • 1
    @JoeDiNottra If you're going down the unrecommended road anyway, you might as well break the unwritten "never manually construct JSON" rule. Just construct the JSON yourself and return it as a String. I doubt any of the built-in serializers allow you to generate a JSON with duplicate keys. But honestly, sometimes you have to sell "no" as a software engineer. If someone asks you to break a diamond with your bare hands, then saying "welp, that's the requirement" isn't really an option either. It's just not feasible. Commented Dec 21, 2022 at 15:04
  • 1
    @Ivar Thank you. I think I'll push back. The client will fight it since "the standard says it's perfectly possible", but I guess it's much safer to avoid this in the long run. Bitter pill to swallow for the client (and for me as well, in consequence). Commented Dec 21, 2022 at 15:06

4 Answers 4

1

You can just return a string with response entity.

String response = "{\n"+
"  \"id\":\"1401\",\n"+
"  \"sku\":\"C18-25\",\n"+
"  \"sku\":\"A15-70\"\n"+
"}";
return new ResponseEntity<String>(response, HttpStatus.OK);

It is not pretty, but it is supported by Spring and will get the job done.

Sign up to request clarification or add additional context in comments.

Comments

0

A HashMap only allows unique key values, so there is no way for you to use HashMap for this purpose. Also while you are correct, you can use duplicate key values in a JSON body, it is not recommended. Why not make sku an array like this?

"sku":["A15-70","C18-25"]

2 Comments

It's a requirement I need to deal with. If I cannot use a Map, what can I use?
I suppose you can try to manually create a JSON string for them and return that? Not ideal, but if you need to do it I guess it works.
0

While creating map create like this

Map<String, List<String>> map = new HashMap<>();

while putting existing key in Map check using containsKey() method and put duplicate value in List of key

you will get result like this

{ "id":"1401", "sku":["A15-70","C18-25"] }

Comments

0

Here's how you can construct a JSON-object from an arbitrary number of Map.Entry instances (but obviously if JSON structure would be more complex it'll be better to avoid generating it manually at all costs since it would be tedious and error-prone):

@SafeVarargs
public static String entriesToJson(Map.Entry<String, String>... entries) {
    
    return Arrays.stream(entries)
        .map(e -> "\t\"%s\" : \"%s\"".formatted(e.getKey(), e.getValue()))
        .collect(Collectors.joining(",\n", "{\n", "\n}"));
}

@GetMapping("/{id}")
@ResponseBody
public ResponseEntity<?> getProduct(@PathVariable String id) {
    // your logic
    return ResponseEntity.ok(
        entriesToJson(
            Map.entry("id", "1401"),
            Map.entry("sku", "C18-25"),
            Map.entry("sku", "A15-70")
        )
    );
}

Usage example:

public static void main(String[] args) {
    System.out.println(entriesToJson(
        Map.entry("id", "1401"),
        Map.entry("sku", "C18-25"),
        Map.entry("sku", "A15-70")
    ));
}

Output:

{
    "id" : "1401",
    "sku" : "C18-25",
    "sku" : "A15-70"
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.