Java 用复数键表示映射的JSON

Java 用复数键表示映射的JSON,java,javascript,json,serialization,Java,Javascript,Json,Serialization,我想将以下(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; Stri

我想将以下(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;
  ...
  ...
}
类机器{
字符串名;
地图部分;
}
类部件描述符{
字符串组;
字符串id;
hashCode()
等于()
}
班级部分{
字符串组;
字符串id;
字符串描述;
字符串兼容;
...
...
}
一台机器的JSON表示是什么

另外(可选),给我指出一个JSON-to-Java序列化程序/反序列化程序,它将支持您的表示

{
  "name": "machine name",
  "parts": [
     { "group": "part group", "id": "part id", "description": "...", ... },
     { "group": "part group", "id": "part id", "description": "...", ... },
     // ...
  ]
}
如果每个部分的“id”是唯一的,那么“parts”属性可以是对象而不是数组,每个部分的“id”作为键

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

我会这么做的。顶级对象的
部分
键将是
JSONObject
JSONArray
,该对象具有
键和
值。
将是一个对象,它是您的
零件描述符
将是您的
零件

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

假设group+id提供唯一的组合,并且“:”是允许的分隔符:

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

            }
}

它可以呈现为下表:

<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>
或者,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:""}]})
参考资料


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

      PartDescriptor
      上的
      toString()
      应该返回要在映射中使用的键。如另一个答案所示,您可以简单地将相关字段连接起来:

      @Override
      public String toString() {
          return group + "|" + id;
      }
      
      然后,当您尝试使用Jackson的
      ObjectMapper对
      机器
      进行序列化时,您将神奇地获得此表单:

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

      JSON要求键是字符串,因此如果您确实需要将数据表示为键(例如,您不想使用数组,如Pointy的答案中所示,因为您希望在合同中保证没有相同键的重复条目)然后,您需要自行决定将复杂键序列化为字符串的方法

      如果使用带分隔符的串联方法(例如
      group1 | part1
      ),需要注意两件事:

      • 您需要一个本身不能出现在关键部件中的分隔符,或者在序列化时需要将其转义(例如,将其加倍)。这样做所防止的问题可能很少发生,但如果这是用可重用的通用代码编写的,那么根据墨菲定律,应该更好地保证这一点——如果出现问题,最终会出现问题
      • 要真正防止“多个值具有相同的复合键”,您需要保持键的顺序相同,例如按字母顺序排序键
      鉴于:

      另外(可选),请指向支持您的表示的JSON-to-Java序列化程序/反序列化程序

      一个值得注意的例子可能是——Google的Java JSON序列化程序库——它使用以下表示:

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

      注意:需要通过设置(默认情况下为off)来启用该功能

      因为PartDescriptor是java映射的关键,我认为我们可以安全地假设group+id组合是唯一的,所以请使用第二个示例。因为JSON在表示类型细节方面的能力非常有限,通用序列化程序通常令人沮丧,并且不适合特定用途。只为自己的数据结构实现自己的序列化程序可能会容易得多。@Pointy谢谢Pointy,我以前没有使用JSON来实现这种目的,我意识到了你的观点。我问这个问题是想听听一些想法,因为我浪费了几个令人沮丧的小时在jackson的未记录和奇怪的API上,结果是一个JavaScript对象文本,而不是JSONobject@PaulSweatte什么意思?这是。事实上,所有的JSON都是JavaScript对象文字…对不起,我的代码复制/粘贴遗漏了一个括号。然而,JSON语法比对象文字语法更具限制性。例如,不允许使用注释或单引号,并且必须引用关键字名称。@Paulste,所以介意删除您的否决票吗?是的,虽然所有JSON都是一个对象文本,但反过来不是真的(我编辑了前面的注释以澄清)。您的解决方案不能反序列化为原始类型。在这种特定情况下,它可能会在json反序列化之后进行一些后处理来反序列化。但我建议不要走这条路,除非json是严格为人类使用的,而且永远不需要进行反序列化。
      {
           "(group1,part1)": { description: ... },
           "(group1,part2)": { description: ... },
           "(group2,part1)": { description: ... },
           ...
           "(groupX,partX)": {description: ... },
      }