如何动态地将JSON转换为数据库表?

时间:2014-07-25 14:20:15

标签: java sql json database

我需要将JSON数据保存到Oracle数据库。 JSON看起来像这样(见下文)。但它不会保持相同的格式。我可能会添加一些额外的节点或修改现有的节点。那么是否可以动态创建或修改oracle表以添加更多列?我打算用Java做到这一点。我将创建一个与JSON匹配的Java类,将JSON转换为Java对象并将其持久保存到表中。但是如何动态修改Java类呢?或者用PL / SQL做到这一点会更好吗? JSON来自移动设备到REST Web服务。

{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}

5 个答案:

答案 0 :(得分:1)

我建议您避免创建新列,而是创建一个新表,其中包含每个新列的一个条目。我在这里假设新列将是菜单项。因此,您将拥有一个包含以下列的“菜单”表:

id    file

你会有一个“menuitem”表,其中包含每个菜单项的一个条目:

id    value    onclick

因此,不是动态添加列,而是添加记录。

答案 1 :(得分:0)

我在评论中建议将您的方法更改为No MongoDB等NoSQL数据库。但是,如果您仍然觉得需要使用关系数据库,那么EAV model可能会指向正确的方向。

总而言之,您将拥有一个“帮助程序”表,用于存储实体所具有的列及其类型。

您不能修改Java类,但可以像Map一样定义类,并实现逻辑以添加所需的列。

PHP产品

Magento在其数据库中使用EAV。

答案 2 :(得分:0)

Mongodb可能是您的最佳选择,或者您可以拥有一个大的TEXT字段,并且只提取您可能搜索的列。

但是,您可以为其他规范化数据创建表,并使用ALTER TABLE添加列。后者可能特别昂贵。

答案 3 :(得分:0)

使用https://github.com/zolekode/json-to-tables/

您在这里:

import json
from core.extent_table import ExtentTable
from core.table_maker import TableMaker

menu = {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}

menu = json.dumps(menu)
menu = json.loads(menu)

extent_table = ExtentTable()
table_maker = TableMaker(extent_table)
table_maker.convert_json_object_to_table(menu, "menu")
table_maker.show_tables(8)
table_maker.save_tables("menu", export_as="sql", sql_connection="your_connection")

输出:

SHOWING TABLES :D

menu
   ID    id value popup
0   0  file  File     0
1   1  None  None  None
____________________________________________________


popup
   ID
0   0
1   1
____________________________________________________


popup_?_menuitem
  ID PARENT_ID is_scalar scalar
0  0         0     False   None
1  1         0     False   None
2  2         0     False   None
____________________________________________________


popup_?_menuitem_$_onclick
   ID  value         onclick PARENT_ID
0   0    New  CreateNewDoc()         0
1   1   Open       OpenDoc()         1
2   2  Close      CloseDoc()         2
3   3   None            None      None
____________________________________________________

答案 4 :(得分:0)

这可以在MYSQL数据库中完成:

此代码采用 JSON 输入字符串并自动生成 SQL Server CREATE TABLE 语句使其更容易 将序列化数据转换为数据库模式。

它并不完美,但应该在开始时提供一个不错的起点 处理新的 JSON 文件。

    SET NOCOUNT ON;
    
    DECLARE 
        @JsonData nvarchar(max) = '
            {
                "Id" : 1,
                "IsActive":true,
                "Ratio": 1.25,
                "ActivityArray":[true,false,true],
                "People" : ["Jim","Joan","John","Jeff"],
                "Places" : [{"State":"Connecticut", "Capitol":"Hartford", "IsExpensive":true},{"State":"Ohio","Capitol":"Columbus","MajorCities":["Cleveland","Cincinnati"]}],
                "Thing" : { "Type":"Foo", "Value" : "Bar" },
                "Created_At":"2018-04-18T21:25:48Z"
            }',
        @RootTableName nvarchar(4000) = N'AppInstance',
        @Schema nvarchar(128) = N'dbo',
        @DefaultStringPadding smallint = 20;
    
    DROP TABLE IF EXISTS ##parsedJson;
    WITH jsonRoot AS (
        SELECT 
            0 as parentLevel, 
            CONVERT(nvarchar(4000),NULL) COLLATE Latin1_General_BIN2 as parentTableName, 
            0 AS [level], 
            [type] ,
            @RootTableName COLLATE Latin1_General_BIN2 AS TableName,
            [key] COLLATE Latin1_General_BIN2 as ColumnName,
            [value],
            ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ColumnSequence
        FROM 
            OPENJSON(@JsonData, '$')
        UNION ALL
        SELECT 
            jsonRoot.[level] as parentLevel, 
            CONVERT(nvarchar(4000),jsonRoot.TableName) COLLATE Latin1_General_BIN2, 
            jsonRoot.[level]+1, 
            d.[type],
            CASE WHEN jsonRoot.[type] IN (4,5) THEN CONVERT(nvarchar(4000),jsonRoot.ColumnName) ELSE jsonRoot.TableName END COLLATE Latin1_General_BIN2,
            CASE WHEN jsonRoot.[type] IN (4) THEN jsonRoot.ColumnName ELSE d.[key] END,
            d.[value],
            ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ColumnSequence
        FROM 
            jsonRoot
            CROSS APPLY OPENJSON(jsonRoot.[value], '$') d
        WHERE 
            jsonRoot.[type] IN (4,5) 
    ), IdRows AS (
        SELECT 
            -2 as parentLevel,
            null as parentTableName,
            -1 as [level],
            null as [type],
            TableName as Tablename,
            TableName+'Id' as columnName, 
            null as [value],
            0 as columnsequence
        FROM 
            (SELECT DISTINCT tablename FROM jsonRoot) j
    ), FKRows AS (
        SELECT 
            DISTINCT -1 as parentLevel,
            null as parentTableName,
            -1 as [level],
            null as [type],
            TableName as Tablename,
            parentTableName+'Id' as columnName, 
            null as [value],
            0 as columnsequence
        FROM 
            (SELECT DISTINCT tableName,parentTableName FROM jsonRoot) j
        WHERE 
            parentTableName is not null
    )
    SELECT 
        *,
        CASE [type]
            WHEN 1 THEN 
                CASE WHEN TRY_CONVERT(datetime2, [value], 127) IS NULL THEN 'nvarchar' ELSE 'datetime2' END
            WHEN 2 THEN 
                CASE WHEN TRY_CONVERT(int, [value]) IS NULL THEN 'float' ELSE 'int' END
            WHEN 3 THEN 
                'bit'
            END COLLATE Latin1_General_BIN2 AS DataType,
        CASE [type]
            WHEN 1 THEN 
                CASE WHEN TRY_CONVERT(datetime2, [value], 127) IS NULL THEN MAX(LEN([value])) OVER (PARTITION BY TableName, ColumnName) + @DefaultStringPadding ELSE NULL END
            WHEN 2 THEN 
                NULL
            WHEN 3 THEN 
                NULL
            END AS DataTypePrecision
    INTO ##parsedJson
    FROM jsonRoot
    WHERE 
        [type] in (1,2,3)
    UNION ALL SELECT IdRows.parentLevel, IdRows.parentTableName, IdRows.[level], IdRows.[type], IdRows.TableName, IdRows.ColumnName, IdRows.[value], -10 AS ColumnSequence, 'int IDENTITY(1,1) PRIMARY KEY' as datatype, null as datatypeprecision FROM IdRows 
    UNION ALL SELECT FKRows.parentLevel, FKRows.parentTableName, FKRows.[level], FKRows.[type], FKRows.TableName, FKRows.ColumnName, FKRows.[value], -9 AS ColumnSequence, 'int' as datatype, null as datatypeprecision FROM FKRows 
    
    -- For debugging:
    -- SELECT * FROM ##parsedJson ORDER BY ParentLevel, level, tablename, columnsequence
    
    DECLARE @CreateStatements nvarchar(max);
    
    SELECT
        @CreateStatements = COALESCE(@CreateStatements + CHAR(13) + CHAR(13), '') + 
        'CREATE TABLE ' + @Schema + '.' + TableName + CHAR(13) + '(' + CHAR(13) +
            STRING_AGG( ColumnName + ' ' + DataType + ISNULL('('+CAST(DataTypePrecision AS nvarchar(20))+')','') +  CASE WHEN DataType like '%PRIMARY KEY%' THEN '' ELSE ' NULL' END, ','+CHAR(13)) WITHIN GROUP (ORDER BY ColumnSequence) 
        + CHAR(13)+')'
    FROM
        (SELECT DISTINCT 
            j.TableName, 
            j.ColumnName,
            MAX(j.ColumnSequence) AS ColumnSequence, 
            j.DataType, 
            j.DataTypePrecision, 
            j.[level] 
        FROM 
            ##parsedJson j
            CROSS APPLY (SELECT TOP 1 ParentTableName + 'Id' AS ColumnName FROM ##parsedJson p WHERE j.TableName = p.TableName ) p
        GROUP BY
            j.TableName, j.ColumnName,p.ColumnName, j.DataType, j.DataTypePrecision, j.[level] 
        ) j
    GROUP BY
        TableName
    
    
    PRINT @CreateStatements;

您可以在 https://bertwagner.com/posts/converting-json-to-sql-server-create-table-statements/

上找到解决方案

在 JAVA 中也可以将 JSON 转换为 POJO 类:

包com.cooltrickshome;
2 导入 java.io.File;
3 导入 java.io.IOException;
4 导入 java.net.MalformedURLException;
5 导入 java.net.URL;
6 导入 org.jsonschema2pojo.DefaultGenerationConfig;
7 导入 org.jsonschema2pojo.GenerationConfig;
8 导入 org.jsonschema2pojo.Jackson2Annotator;
9 导入 org.jsonschema2pojo.SchemaGenerator;
10 导入 org.jsonschema2pojo.SchemaMapper;
11 导入 org.jsonschema2pojo.SchemaStore;
12 导入 org.jsonschema2pojo.SourceType;
13 导入 org.jsonschema2pojo.rules.RuleFactory;
14 导入 com.sun.codemodel.JCodeModel;
15 公共类 JsonToPojo {
16 /**
17 * @param 参数
18 */
19 public static void main(String[] args) {
20 String packageName="com.cooltrickshome";
21 File inputJson= new File("."+File.separator+"input.json");
22 文件 outputPojoDirectory=new File("."+File.separator+"convertedPojo");
23 outputPojoDirectory.mkdirs();
24 试试{
25 new JsonToPojo().convert2JSON(inputJson.toURI().toURL(), outputPojoDirectory, packageName, inputJson.getName().replace(".json", ""));
26 } catch (IOException e) {
27 // TODO 自动生成的catch块
28 System.out.println("转pojo时遇到问题:"+e.getMessage());
29 e.printStackTrace();
30 }
31 }
32 public void convert2JSON(URL inputJson, File outputPojoDirectory, String packageName, String className) 抛出 IOException{
33 JCodeModel codeModel = new JCodeModel();
34 URL 源 = inputJson;
35 GenerationConfig config = new DefaultGenerationConfig() {
36 @覆盖
37 public boolean isGenerateBuilders() { // 通过覆盖方法设置配置选项
38 返回真;
39 }
40 public SourceType getSourceType(){
41 返回 SourceType.JSON;
42 }
43 };
44 SchemaMapper mapper = new SchemaMapper(new RuleFactory(config, new Jackson2Annotator(config), new SchemaStore()), new SchemaGenerator());
45 mapper.generate(codeModel, className, packageName, source);
46 codeModel.build(outputPojoDirectory);
47 }
48 }