如何将平面地图转换为复杂的JSONObject?

时间:2018-02-07 16:33:08

标签: java json

我想将平面键值映射转换为复杂的json对象。

结构如下:

  • map-keys当然代表json键。
  • 嵌套元素用点
  • 分隔
  • 列表元素由密钥末尾的数字发出信号。最后可能有多个数字。不幸的是,键按相反的顺序排列。意味着第一个"关键片段"映射到最后一个数字,最里面的键片段映射到第一个数字。

以下示例:

service.fee.1.1=a
service.fee.2.1=b
service.fee.3.1=c

这里的"服务"键映射始终为index = 1。这意味着"服务"是一个数组,但在这种情况下只有一个元素。 一个元素有关键"费用",里面有3个值。

结果json应为:

{
    "service": [
        {
            "fee": ["a", "b", "c"]
        }
    ]
}

另一个例子:

service.fee.name.1.1=a
service.fee.age.2.1=b
service.fee.test.2.1=c

{
    "service": [
        {
            "fee": [
                {
                    "name": "a"
                },
                {
                    "age": "b",
                    "test": "c"
                }
            ]
        }
    ]
}

这就是我的开始,但我无法理解我可能必须使用递归来处理嵌套对象和列表:

JSONObject json = new JSONObject();

for (Map.Entry<String, String> entry : map.entrySet()) {
    String key = entry.getKey();
    if (endswithdigit(key)) {

    } else {
        if (key.contains("-")) {
            //complex object
            JSONObject subjson = new JSONObject();
            json.put(key, subjson);

            //TODO probably have to apply some kind of recursion here with subjson??
        } else {
            //plain value
            json.put(key, entry.getValue());
        }
    }
}

也许您可以建议如何使用嵌套列表和递归正确构建嵌套JSONObject

3 个答案:

答案 0 :(得分:1)

如果您需要自己解决这个问题(IE:库无法处理它),那么我会将其分解,以便可以使用Composite Pattern进行连贯处理。

我将分两部分来回答这个问题:第一,提出创建层次结构的解决方案;第二,如何利用Composite模式将内部层次转换为您想要的JSON。

第1部分:创建Heirarhcy

这种方法的一种方法是通过将元素划分为二进制来迭代地创建对象(从包含每个元素的公共复合根对象开始)。这将形成数据的复合结构。流程将是:

  • 对于对象合成的bin中的每个元素:
    • 从元素左侧剥离顶级标识符
    • 创建与其关联的标识符。
    • 如果键入:
      • 从右侧剥离钥匙
      • 为标识符创建复合数组(如果它不存在)。
      • 如果元素的=还有剩余数据:
        • 为与该数组索引关联的元素bin创建复合对象(如果它不存在)。
        • 将元素放在该对象的bin中。
      • 否则为索引值创建叶节点。
    • 否则,如果元素的=还有剩余数据:
      • 为与该数组索引关联的元素bin创建复合对象(如果它不存在)。
      • 将元素放在该对象的bin中。
    • 否则,请为标识符的值创建叶节点。
  • 重复所有新垃圾箱。

例如,假设我们正在使用给定的数据集:

x.y.z.1.1=A
x.y.z.3.1=B
x.y.w.1.1=C
x.u.1=D
a.b.1=E
a.c.1=F
e.1=G
e.2=H
i=I
m.j=J
m.k=K

该过程将遵循:

ITERATION 0 (initialize):
root // x.y.z.1.1=A, x.y.z.3.1=B, x.y.w.1.1=C, x.u.1=D, a.b.1=E, a.c.1=F, e.1=G, e.2=H, i=I, m.j=J, m.k=K

ITERATION 1:
root :
  x[1] // y.z.1=A, y.z.3=B, y.w.1=C, u=D
  a[1] // b=E, c=F
  e[1] : "G"
  e[2] : "H"
  i : "I"
  m : // j=J, k=K

ITERATION 2:
root :
  x[1] : 
    y[1] // z=A, w=C
    y[3] // z=B
    u : "D"
  a[1] :
    b : "E"
    c : "F"
  e[1] : "G"
  e[2] : "H"
  i : "I"
  m : 
    j : "J"
    k : "K"

ITERATION 3:
root :
  x[1] :
    y[1] :
      z : "A"
      w : "C"
    y[3] :
      z : "B"
    u: "D"
  a[1] :
    b : "E"
    c : "F"
  e[1] : "G"
  e[2] : "H"
  i : "I"
  m : 
    j : "J"
    k : "K"

第2部分:复合模式

此时,我们已经迭代地将我们的数据划分为一个复合结构;现在我们只需要将内部化的数据结构转换为JSON。这就是复合模式将派上用场的地方;您的每个对象都将实现以下接口:

// All objects in the composite tree must implement this.
public interface Jsonable {
    // The non-leaf objects will need to have their implementation of this
    // call it for each child object (and handle gaps).
    JsonObject toJsonObject(); 
}

如果遵循上述规定,我们可能会有三个此接口的实现:ArrayCompositeObjectCompositeValueLeaf

在您的根元素上调用toJsonObject()将为您提供完整的JsonObject。以下示例的结构表示如下(注意y数组中添加的间隙;这需要在数组合成的toJsonObject()调用中处理):

{
  "x" : [
    {
      "y" : [
        {
          "z" : "A",
          "w" : "C"
        },
        "",
        {
          "z" : "B"
        }
      ]
    }
  ],
  "a" : [
    {
      "b" : "D",
      "c" : "E"
    }
  ],
  "e" : [
    "F",
    "G"
  ]
  "i" : "I"
  "m" : {
    "j" : "J",
    "k" : "K"
  }
}

忽略白色间距似乎是你正在寻找的东西。

请注意,这假定数据集不包含会导致无效JSON的元素。 IE:数据集不能包含以下内容:

i=I
i.1=I

因为它会说i既是数组又是值。

答案 1 :(得分:0)

请尝试使用Gson库并使用新的Gson()。toJson(yourmap);这会将您的地图转换为JSON格式。

答案 2 :(得分:0)

也许你通过拆分数字开始的键,并使用子键的LIFO和值和索引的FIFO来解决它。而不是拆分它可以通过解析密钥并检测数字的开始位置来完成:

例如:

x.y.z.2.1 = val

这是分开的,以显示它是如何工作的,但只需解析字符串就可以完成(:是分隔分隔)。

x.y.z : 2.1 : val

然后将子键放入LIFO(x先进入,最后进行z):

LIFO
head: z
      y
      x

和值和索引的FIFO(2先进入,val进去最后)

Fifo
top:2
    1
    val

然后你可以从LIFO中弹出并将它与FIFO的弹出相匹配。第一个赋值将是地图的值,然后,分配将完成到最后一步的对象或数组。

z = val
y[1] = z
x[2] = y