Storing a JSON schema in mongodb with spring

2019-02-22 12:51发布

I am new to Spring data and mongodb. I have a JSON object which represents a JSON Schema and I need to store that in mongodb using spring data. But the issue with JSON schema is the structure of JSON Schema is dynamic; for example below are two valid JSON schema with completely different structure.

{
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "minLength": 10
        },
        "age": {
            "type": "integer"
        }
    },
    "required": [
        "name",
        "age"
    ]
}

{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "abc": {
                "type": "boolean"
            },
            "xyz": {
                "$ref": "#/definitions/"
            },
            "asd": {
                "type": "null"
            }
        },
        "required": [
            "abc",
            "xyz"
        ]
    }
}

How can I define a JAVA POJO Class so that I can map the above JSON with the defined class and store it in mongodb. Or is it possible to do CURD operation in spring without mapping it to a POJO class?

5条回答
SAY GOODBYE
2楼-- · 2019-02-22 13:01

Please find here the necessary code.

@lombok.Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Bounty {

  String type;
  Map<String, Object> items;
  Map<String, Object> properties;
  List<Object> required;
}

Here is my repository class

public interface BountyRepository extends MongoRepository<Bounty, String> {
}

And here is a controller snippet which u can use to try it out

@GetMapping("/insert/{number}")
    public void insert(@PathVariable int number){
        bountyRepository.save(getBounty(number));
    }


    public Bounty getBounty(int number){
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString1 = "{\n" +
            "    \"type\": \"object\",\n" +
            "    \"properties\": {\n" +
            "        \"name\": {\n" +
            "            \"type\": \"string\",\n" +
            "            \"minLength\": 10\n" +
            "        },\n" +
            "        \"age\": {\n" +
            "            \"type\": \"integer\"\n" +
            "        }\n" +
            "    },\n" +
            "    \"required\": [\n" +
            "        \"name\",\n" +
            "        \"age\"\n" +
            "    ]\n" +
            "}";


        String jsonString2 = "{\n" +
            "    \"type\": \"array\",\n" +
            "    \"items\": {\n" +
            "        \"type\": \"object\",\n" +
            "        \"properties\": {\n" +
            "            \"abc\": {\n" +
            "                \"type\": \"boolean\"\n" +
            "            },\n" +
            "            \"xyz\": {\n" +
            "                \"$ref\": \"#/definitions/\"\n" +
            "            },\n" +
            "            \"asd\": {\n" +
            "                \"type\": \"null\"\n" +
            "            }\n" +
            "        },\n" +
            "        \"required\": [\n" +
            "            \"abc\",\n" +
            "            \"xyz\"\n" +
            "        ]\n" +
            "    }\n" +
            "}";

        try {
            Bounty bounty1 = objectMapper.readValue(jsonString1, Bounty.class);
            Bounty bounty2 = objectMapper.readValue(jsonString2, Bounty.class);


            if (number == 1) return bounty1;
            if (number == 2) return bounty2;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

This is how it looks like in Mongo after save.

/* 1 */
{
    "_id" : ObjectId("58da2390fde4f133178499fa"),
    "_class" : "pani.kiran.sumne.model.Bounty",
    "type" : "object",
    "properties" : {
        "name" : {
            "type" : "string",
            "minLength" : 10
        },
        "age" : {
            "type" : "integer"
        }
    },
    "required" : [ 
        "name", 
        "age"
    ]
}

/* 2 */
{
    "_id" : ObjectId("58da23adfde4f133178499fb"),
    "_class" : "pani.kiran.sumne.model.Bounty",
    "type" : "array",
    "items" : {
        "type" : "object",
        "properties" : {
            "abc" : {
                "type" : "boolean"
            },
            "xyz" : {
                "$ref" : "#/definitions/"
            },
            "asd" : {
                "type" : "null"
            }
        },
        "required" : [ 
            "abc", 
            "xyz"
        ]
    }
}
查看更多
来,给爷笑一个
3楼-- · 2019-02-22 13:04

In my project I had a very dynamic structure of my models and I mapped them by using a java.util.Map object

this is how my mondo document model has been implemented:

@Document(collection = "e_form_data")
public class FormDataModel extends AbstractModel
{
    private static final long serialVersionUID = -1733975205300782871L;
    @Field
    @Indexed(name = "e_form_id_idx")
    private String eFormId;
    @Field
    private Map<String, Object> eFormData;

    public FormDataModel()
    {
        super();
    }

    public FormDataModel(String id, String creatoDa, String modificatoDa, Date dataCreazione, Date dataModifica, String eFormId, Map<String, Object> eFormData)
    {
        super(id, creatoDa, modificatoDa, dataCreazione, dataModifica);
        this.eFormData = eFormData;
        this.eFormId = eFormId;
    }

    public FormDataModel(Map<String, Object> eFormData)
    {
        super();
        this.eFormData = eFormData;
    }

    public Map<String, Object> geteFormData()
    {
        return eFormData;
    }

    public void seteFormData(Map<String, Object> eFormData)
    {
        this.eFormData = eFormData;
    }

    public String geteFormId()
    {
        return eFormId;
    }

    public void seteFormId(String eFormId)
    {
        this.eFormId = eFormId;
    }

    public String getDataInserimento()
    {
        return Utils.formatDateTime(new DateTime(this.dataCreazione.getTime()), "dd/MM/yyyy");
    }

    @Override
    public String toString()
    {
        return "FormDataModel [eFormId=" + eFormId + ", eFormData=" + eFormData + "]";
    }

}

By using this all works pretty good

查看更多
小情绪 Triste *
4楼-- · 2019-02-22 13:14

You can map embedded documents using @DBref

@Document(collection = "first")
public class First {

    @Id
    private String id;

    @DBRef
    private Properties properties;

    @Field
    private List<String> required;

    // constructor
    // getters and setter    
}

public class Properties {

    @Id
    private String id;

    @DBRef
    private Name name;

    @DBRef
    private Age age;

    // constructor
    // getters and setter   
}

public class Name { ... }
public class Age { ... }

http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb

http://docs.spring.io/spring-data/data-mongo/docs/1.4.2.RELEASE/reference/html/mapping-chapter.html#mapping-usage-references

Or as Angelo Immediata suggested

@Document(collection = "first")
public class First {

    @Id
    private String id;

    @Field
    private Map<String, Object> properties;

    @Field
    private List<String> required;

    // constructor
    // getters and setter    
}

And you will need some custom read and write converters

http://docs.spring.io/spring-data/data-mongo/docs/1.4.2.RELEASE/reference/html/mapping-chapter.html#mapping-explicit-converters

查看更多
太酷不给撩
5楼-- · 2019-02-22 13:21

I would recommend using MongoTemplate and serialize and deserailize using Gson/Jackson.

Mongo Template have CRUD methods which takes collection name and DBObject entity which is very similar to if you were to directly use mongo java driver.

So you will have json payload and using one of the mapper library to convert them into Map.

Something like

Deserialise

ObjectMapper mapper = new ObjectMapper(); 
TypeReference<HashMap<String,Object>> typeRef 
        = new TypeReference<HashMap<String,Object>>() {};
HashMap<String,Object> map = mapper.readValue(jsonpayload, typeRef); 

DBObject

DBObject dbObject = new BasicDBObject(map);

MongoTemplate

mongoTemplate.save(dbObject, "collectionname");

You can do something similar for all other CRUD operations.

查看更多
迷人小祖宗
6楼-- · 2019-02-22 13:22

FWIW, MongoDB 3.6 introduced JSON Schema Validation support at the database level. You can read more on MongoDB's blog. Hope that helps a bit!

查看更多
登录 后发表回答