【问题标题】:JSON Representation of Map with Complex Key具有复杂键的地图的 JSON 表示
【发布时间】:2012-06-10 10:56:43
【问题描述】:

我想将以下 (java) 数据结构序列化为 JSON:

class Machine {
  String name;
  Map<PartDescriptor, Part> parts;
}

class PartDescriptor {
  String group;
  String id;

  hashCode()
  equals()
}

class Part {
  String group;
  String id;
  String description;
  String compat;
  ...
  ...
}

Machine 的 JSON 表示形式是什么?

另外(可选),请指出一个 JSON 到 Java 序列化器/反序列化器,它将支持您的表示

【问题讨论】:

  • 由于 JSON 表达类型细节的能力非常有限,通用序列化器通常令人沮丧并且不足以满足特定目的。为自己的数据结构实现自己的序列化器可能会容易得多。
  • @Pointy 谢谢 Pointy,我以前没有将 JSON 用于这种目的,我意识到了你的观点。我问这个问题是为了听听一些想法,因为我在杰克逊的无证和奇怪的 api 上浪费了几个令人沮丧的时间

标签: java javascript json serialization


【解决方案1】:

我会这样做:

{
  "name": "machine name",
  "parts": [
     { "group": "part group", "id": "part id", "description": "...", ... },
     { "group": "part group", "id": "part id", "description": "...", ... },
     // ...
  ]
}

如果每个Part的“id”是唯一的,那么“parts”属性可以是一个对象而不是数组,每个part的“id”作为key。

{
  "name": "machine name",
  "parts": {
     "1st part id": { "group": "part group", "description": "...", ... },
     "2nd part id": { "group": "part group", "description": "...", ... },
     // ...
  }
}

【讨论】:

  • 由于 PartDescriptor 是 java map 的关键,我认为我们可以安全地假设 group+id 组合是唯一的,所以使用你的第二个示例。
【解决方案2】:

您不需要注释或自定义序列化程序。假设您已经为PartMachine 中的所有字段设置了getter,那么真正缺少的只是PartDescriptor 上的toString()。如果由于某种原因您没有 getter 函数,则需要使用 @JsonProperty 注释感兴趣的字段,以便 Jackson 知道要包含在序列化输出中的字段。但是,最好(也更容易)简单地创建 getter。

PartDescriptor 上的 toString() 应返回您要在映射中使用的密钥。正如另一个答案所暗示的那样,您可以简单地连接相关字段:

@Override
public String toString() {
    return group + "|" + id;
}

然后,当您尝试用 Jackson 的 ObjectMapper 序列化 Machine 时,您会神奇地得到这个表单:

{
  "name" : "Toaster",
  "parts" : {
    "Electrical|Descriptor1" : {
      "group" : "Electrical",
      "id" : "Part1",
      "description" : "Heating Element",
      "compat" : "B293"
    },
    "Exterior|Descriptor2" : {
      "group" : "Exterior",
      "id" : "Part2",
      "description" : "Lever",
      "compat" : "18A"
    }
  }
}

【讨论】:

  • 结果是 JavaScript 对象字面量,而不是 JSON 对象
  • @PaulSweatte 你是什么意思?这是valid JSON。事实上,所有 JSON 都是 JavaScript 对象字面量...
  • 抱歉,我复制/粘贴的代码遗漏了一个括号。然而,JSON 语法比对象字面量语法更严格。例如,不允许使用 cmets 或单引号,并且必须引用键名。
  • @PaulSweatte 所以介意删除你的反对票吗?是的,虽然所有 JSON 都是对象文字,但反之则不正确(我编辑了我之前的评论以澄清)。
  • 您的解决方案无法反序列化为原始类型。在这种特定情况下,它可能会通过在 json 反序列化之后进行一些后处理来反序列化。但我建议不要走这条路,除非 json 严格用于人类消费,并且不需要进行反序列化。
【解决方案3】:

我会这样做。顶级对象的parts 键是JSONObject 中的JSONArray,具有keyvaluekey 是您的 PartDescriptor 对象,value 是您的 Part

{
    "name":"theName",
    "parts":[
        {
            "key":{
                       "group":"theGroup",
                       "id":"theId"
                  },
            "value":{
                       "group":"theGroup",
                       "id":"theId",
                       "description":"theDescription",
                       "compat":"theCompat",
                       ...
                    }
        },
        ...
    ]
}

【讨论】:

    【解决方案4】:

    假设 group+id 给出了一个唯一的组合,并且“:”是一个允许的分隔符:

    {  
       "name": "machine name",
       "parts": { 
                   "somegroup:01465": {
                                        "group":"somegroup",
                                        "id": "01465",
                                        ...
                                      },
                   "othergroup:32409": {
                                         "group":"othergroup",
                                         "id": "32409",
                                         ...
                                       }
    
                }
    }
    

    【讨论】:

      【解决方案5】:

      JSON 要求密钥是一个字符串,因此如果您确实需要将数据表示为键控(例如,您不想使用数组,就像 Pointy 的回答中那样,因为您想保证它 在合同中没有具有相同键的重复条目)那么您需要自己决定将复杂键序列化为字符串的方法。

      如果采用使用连接与分隔符的方法(例如group1|part1),需要注意两点:

      • 您需要一个本身不能出现在关键部分的分隔符,或者在序列化时需要对其进行转义(例如,将其加倍)。这防止的问题可能很少会遇到,但如果要以可重用的通用代码编写,根据墨菲定律,最好保证它 - 如果出现问题,最终会出现问题。
      • 要真正防止“具有相同复合键的多个值”,您需要保持键的相同顺序,例如按字母顺序对键进行排序

      鉴于:

      另外(可选),请指向一个 JSON 到 Java 序列化器/反序列化器,它将支持您的表示

      一个值得注意的例子可能是Gson - Google 的 Java JSON 序列化程序库 - 它使用这种表示:

      {
           "(group1,part1)": { description: ... },
           "(group1,part2)": { description: ... },
           "(group2,part1)": { description: ... },
           ...
           "(groupX,partX)": {description: ... },
      }
      

      注意:该功能需要通过设置enableComplexMapKeySerialization开启(默认关闭for backwards compatibility

      【讨论】:

        【解决方案6】:

        可以如下表所示:

        <table class="machine" name="">
           <tr>
             <th class="partdescriptor" colspan="2">
             <th class="part" colspan="4">
           </tr>
           <tr>
             <td class="partdescriptor group"></td>
             <td class="partdescriptor" id=""></td>
             <td class="part group"></td>
             <td class="part" id=""></td>
             <td class="description"></td>
             <td class="compat"></td>
            </tr>
         </table>
        

        由于缺少通过属性的元数据,标记分解为以下 JSON 对象:

        {
            "HTMLTableElement": 
            [
                {
                    "classname": "machine",
                    "name": ""
                },
                {
                    "HTMLTableRowElement": 
                    [
                        {
                            "HTMLTableCellElement": {"classname":"partdescriptor","colspan":2}
                        },
                        {
                            "HTMLTableCellElement": {"classname":"part","colspan":4}
                        }
                    ]
                },
                {
                    "HTMLTableRowElement": 
                    [
                        {
                            "HTMLTableCellElement": {"classname":"partdescriptor group"}
                        },
                        {
                            "HTMLTableCellElement": {"classname":"partdescriptor","id":""}
                        },
                        {
                            "HTMLTableCellElement": {"classname":"part","id":""}
                        },
                        {
                            "HTMLTableCellElement": {"classname":"description"}
                        },
                        {
                            "HTMLTableCellElement": {"classname":"compat"}
                        }
                    ]
                }
            ]
        }
        

        另外,Unicode 可以简化映射:

        {"name":"","[{\u0022group\u0022:\u0022\u0022},{\u0022id\u0022:\u0022\u0022}]":
         [
         {"group":""},
         {"id":""},
         {"description":""},
         {"compat":""}
         ]
        }
        

        可以字符串化的:

        JSON.stringify({"name":"","[{\u0022group\u0022:\u0022\u0022},{\u0022id\u0022:\u0022\u0022}":[{"group":""},{"id":""},{"description":""},{"compat":""}]})
        

        生产:

        "{\"name\":\"\",\"[{\\\"group\\\":\\\"\\\"},{\\\"id\\\":\\\"\\\"}]\":[{\"group\":\"\"},{\"id\":\"\"},{\"description\":\"\"},{\"compat\":\"\"}]}"
        

        可以解析的:

        JSON.parse("{\"name\":\"\",\"[{\\\"group\\\":\\\"\\\"},{\\\"id\\\":\\\"\\\"}]\":[{\"group\":\"\"},{\"id\":\"\"},{\"description\":\"\"},{\"compat\":\"\"}]}")
        

        产生一个对象字面量:

        ({name:"", '[{"group":""},{"id":""}]':[{group:""}, {id:""}, {description:""}, {compat:""}]})
        

        参考文献

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-03-22
          • 1970-01-01
          • 1970-01-01
          • 2023-03-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多