Home > OS >  How to receive a Map into a Spring Boot Application from outside (UI/Postman)
How to receive a Map into a Spring Boot Application from outside (UI/Postman)

Time:09-18

The following is my Controller that has two methods.
First method receives a List of Products and second one receives a Map of Products with UniqueID as Key.
I'm able to receive and use the List version perfectly (it is working fine).
But not able to succeed with Map version. Please correct me where I was wrong.

My Controller class is as follows....

@RestController
public class MyBoot1Controller {

    @PostMapping(value = "/storeList")
    public String storeProducts(@RequestBody List<Product> products) {
        System.out.println("Received Products count: "   products.size());
        System.out.println(products);
        return "received "   products.size()   " products into the List.";
    }

    @PostMapping(value = "/storeMap")
    public String storeProducts(@RequestBody Map<UniqueID, Product> products) {
        System.out.println("Received Products count: "   products.size());
        System.out.println(products);
        return "received "   products.size()   " products into the Map.";
    }
}

The Class definitions of Product and UniqueID are as follows....

public class Product {
    int pid;
    String pname;
    String ptype;
    int plife;
    //getters & setters
}

final public class UniqueID {
    public String uid;

    public UniqueID(String uid) {
        super();
        this.uid = uid;
    }

    @Override
    public String toString() {
        return uid;
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    @Override
    public boolean equals(Object u) {
        UniqueID ud = (UniqueID) u;
        return (ud.uid == uid);
    }

    @Override
    public int hashCode() {
        int h = 0, i;
        for (i = 0; i < uid.length(); i  )
            h  = (i   1) * uid.charAt(i);
        h  = uid.length();
        return h;
    }
}

The sample data that I was passing from Postman with POST method in each (List and Map) version is as below.

The following is the JSON input For List (working fine...)

[
    {
        "pid": 1,
        "pname": "laptop",
        "ptype": "computer",
        "plife": 10
    },
    {
        "pid": 2,
        "pname": "keyboard",
        "ptype": "computer",
        "plife": 12
    },
    {
        "pid": 3,
        "pname": "Plastic Flowers",
        "ptype": "Decoration",
        "plife": 3
    }
]

The following is the JSON input For Map (not working)

{   
    {
        "uid":"first"
    }:
    {
        "pid": 1,
        "pname": "laptop",
        "ptype": "computer",
        "plife": 10
    },

    {
        "uid":"second"
    }:
    {
        "pid": 2,
        "pname": "keyboard",
        "ptype": "computer",
        "plife": 12
    },


    {
        "uid":"third"
    }:
    {
        "pid": 3,
        "pname": "Plastic Flowers",
        "ptype": "Decoration",
        "plife": 3
    }
}

The following JSON is working fine when I use a String (Instead of UniqueID) as key...

{
  "first": {
    "pid": 1,
    "pname": "laptop",
    "ptype": "elec",
    "plife": 10
  },
  "second": {
    "pid": 2,
    "pname": "phone",
    "ptype": "elec",
    "plife": 10
  },
  "third": {
    "pid": 3,
    "pname": "purse",
    "ptype": "leather",
    "plife": 20
  },
  "fourth": {
    "pid": 4,
    "pname": "book",
    "ptype": "stationery",
    "plife": 100
  }
}

If I have only one field in Key (UniqueID), I can send a String for that.

I understand that my JSON format above (for Map version) for Key is invalid but what if my Key (UniqueID) has multiple data elements like the following?

final public class UniqueID {
    public String uid;
    public String personalNumber;
    public String citizenshipNumber;
}

CodePudding user response:

The second JSON you're showing is invalid JSON, so no framework will be able to deserialize that.

To answer your question about what you should do if your unique ID contains multiple fields, then the answer is to use a different JSON structure, for example:

[
  {
    "id": {
      "uid": 1,
      "personalNumber": 1,
      "citizenshipNumber": 1
    },
    "product": {
      "pid": 1,
      "pname": "laptop",
      "ptype": "computer",
      "plife": 10      
    }
  }
]

Now you can create an additional class like this:

public class ProductWithID {
    private UniqueID id;
    private Product product;
    // TODO: Implement getters   setters
}

And in your controller you can use List<ProductWithID>:

@PostMapping(value = "/storeList")
public String storeProducts(@RequestBody List<ProductWithID> products) {
    // TODO: Implement
}

If you prefer to use a Map, you can do the translation in your code. For example:

@PostMapping(value = "/storeList")
public String storeProducts(@RequestBody List<ProductWithID> products) {
    Map<UniqueID, Product> productMap = products
        .stream()
        .toMap(ProductWithID::getId, ProductWithID::getProduct);
    // TODO: Implement
}

CodePudding user response:

First you would need to make your json valid. I would propose this structure:

{
  "{\"uid\": \"first\"}": {
    "pid": 1,
    "pname": "laptop",
    "ptype": "computer",
    "plife": 10
  },
  "{\"uid\":\"second\"}": {
    "pid": 2,
    "pname": "keyboard",
    "ptype": "computer",
    "plife": 12
  },
  "{\"uid\":\"third\"}": {
    "pid": 3,
    "pname": "Plastic Flowers",
    "ptype": "Decoration",
    "plife": 3
  }
}

Note that the keys are stings, as they should be, but they UniqueID serialized as json.

I am assuming jackson, since question is tagged as spring-boot. First you need to create KeyDeserializer:

private static final class TestDeserializer extends KeyDeserializer {
    
    private static final ObjectMapper MAPPER = new ObjectMapper();
    
    public TestDeserializer() {
        super();
    }
    @Override
    public Object deserializeKey(String key, DeserializationContext context) throws IOException {
        return MAPPER.readValue(key, UniqueID.class);
    }
}

And register it for UniqueID:

@JsonDeserialize(keyUsing = TestDeserializer.class)
public class UniqueID {
    public String uid;
    public String personalNumber;
    public String citizenshipNumber;
}

Simple main method to test:

public class Temp {

    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        Map<UniqueID, Product> map = objectMapper.readValue(json, TypeFactory.defaultInstance().constructParametricType(Map.class, UniqueID.class, Product.class));
        System.out.println(map);
    }
}
  • Related