Skip to content

Commit feafa4b

Browse files
authored
Merge pull request #754 from bserdar/any-support
Fix #753: add any type, unsearchable free-form json data
2 parents 8e5a063 + d5f6a16 commit feafa4b

File tree

4 files changed

+273
-0
lines changed

4 files changed

+273
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
Copyright 2013 Red Hat, Inc. and/or its affiliates.
3+
4+
This file is part of lightblue.
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package com.redhat.lightblue.metadata.types;
20+
21+
import java.io.Serializable;
22+
23+
import com.fasterxml.jackson.databind.JsonNode;
24+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25+
26+
import com.redhat.lightblue.metadata.Type;
27+
import com.redhat.lightblue.metadata.MetadataConstants;
28+
29+
import com.redhat.lightblue.util.JsonUtils;
30+
31+
public final class AnyType implements Type, Serializable {
32+
33+
private static final long serialVersionUID = 1l;
34+
35+
public static final Type TYPE = new AnyType();
36+
public static final String NAME = "any";
37+
38+
@Override
39+
public String getName() {
40+
return NAME;
41+
}
42+
43+
@Override
44+
public boolean supportsEq() {
45+
return false;
46+
}
47+
48+
@Override
49+
public boolean supportsOrdering() {
50+
return false;
51+
}
52+
53+
@Override
54+
public JsonNode toJson(JsonNodeFactory factory, Object obj) {
55+
return JsonUtils.toJson(obj);
56+
}
57+
58+
@Override
59+
public Object fromJson(JsonNode node) {
60+
return JsonUtils.fromJson(node);
61+
}
62+
63+
@Override
64+
public Object cast(Object obj) {
65+
if (obj == null) {
66+
return null;
67+
} else if (obj instanceof JsonNode) {
68+
return fromJson((JsonNode)obj);
69+
} else {
70+
return obj;
71+
}
72+
}
73+
74+
@SuppressWarnings({"rawtypes", "unchecked"})
75+
@Override
76+
public int compare(Object v1, Object v2) {
77+
if (v1 == null) {
78+
if (v2 == null) {
79+
return 0;
80+
} else {
81+
return -1;
82+
}
83+
} else if (v2 == null) {
84+
return 1;
85+
} else {
86+
throw new UnsupportedOperationException(MetadataConstants.ERR_COMPARE_NOT_SUPPORTED);
87+
}
88+
}
89+
90+
@Override
91+
public boolean equals(Object obj) {
92+
return obj instanceof AnyType;
93+
}
94+
95+
@Override
96+
public int hashCode() {
97+
return NAME.hashCode();
98+
}
99+
100+
@Override
101+
public String toString() {
102+
return NAME;
103+
}
104+
105+
private AnyType() {
106+
}
107+
}

metadata/src/main/java/com/redhat/lightblue/metadata/types/DefaultTypes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public DefaultTypes() {
4646
typeMap.put(ArrayType.NAME, ArrayType.TYPE);
4747
typeMap.put(ObjectType.NAME, ObjectType.TYPE);
4848
typeMap.put(ReferenceType.NAME, ReferenceType.TYPE);
49+
typeMap.put(AnyType.NAME, AnyType.TYPE);
4950
typeMap.put(UIDType.NAME, UIDType.TYPE);
5051
}
5152
}

util/src/main/java/com/redhat/lightblue/util/JsonUtils.java

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,18 @@
2424
import java.io.InputStream;
2525
import java.io.InputStreamReader;
2626
import java.io.Reader;
27+
28+
import java.math.BigDecimal;
29+
import java.math.BigInteger;
30+
2731
import java.nio.charset.Charset;
32+
2833
import java.util.Iterator;
2934
import java.util.Map;
35+
import java.util.Collection;
36+
import java.util.ArrayList;
37+
import java.util.Date;
38+
import java.util.HashMap;
3039

3140
import org.apache.commons.lang3.text.StrSubstitutor;
3241

@@ -36,6 +45,12 @@
3645
import com.fasterxml.jackson.databind.ObjectMapper;
3746
import com.fasterxml.jackson.databind.node.ArrayNode;
3847
import com.fasterxml.jackson.databind.node.ObjectNode;
48+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
49+
import com.fasterxml.jackson.databind.node.ValueNode;
50+
import com.fasterxml.jackson.databind.node.NullNode;
51+
import com.fasterxml.jackson.databind.node.TextNode;
52+
import com.fasterxml.jackson.databind.node.BooleanNode;
53+
import com.fasterxml.jackson.databind.node.NumericNode;
3954
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
4055
import com.github.fge.jsonschema.core.report.ProcessingMessage;
4156
import com.github.fge.jsonschema.core.report.ProcessingReport;
@@ -108,6 +123,126 @@ public static JsonNode json(Reader reader, boolean systemPropertySubstitution) t
108123
return json(bld.toString(), systemPropertySubstitution);
109124
}
110125

126+
/**
127+
* Returns a Java object for a json value node based on the node type.
128+
*/
129+
public static Object valueFromJson(ValueNode node) {
130+
if (node instanceof NullNode) {
131+
return null;
132+
} else {
133+
if(node instanceof TextNode) {
134+
return node.textValue();
135+
} else if(node instanceof BooleanNode) {
136+
return node.booleanValue();
137+
} else if(node instanceof NumericNode) {
138+
return node.numberValue();
139+
} else {
140+
throw new RuntimeException("Unsupported node type:"+node.getClass().getName());
141+
}
142+
}
143+
}
144+
145+
/**
146+
* Returns a Json value node for the given value. Dates are converted to strings
147+
*/
148+
public static ValueNode valueToJson(Object value) {
149+
if(value==null) {
150+
return JsonNodeFactory.instance.nullNode();
151+
} else if(value instanceof String) {
152+
return JsonNodeFactory.instance.textNode((String)value);
153+
} else if(value instanceof Number) {
154+
if (value instanceof BigDecimal) {
155+
return JsonNodeFactory.instance.numberNode((BigDecimal) value);
156+
} else if (value instanceof BigInteger) {
157+
return JsonNodeFactory.instance.numberNode((BigInteger) value);
158+
} else if (value instanceof Double) {
159+
return JsonNodeFactory.instance.numberNode((Double) value);
160+
} else if (value instanceof Float) {
161+
return JsonNodeFactory.instance.numberNode((Float) value);
162+
} else if (value instanceof Long) {
163+
return JsonNodeFactory.instance.numberNode((Long) value);
164+
} else {
165+
return JsonNodeFactory.instance.numberNode( ((Number)value).intValue());
166+
}
167+
} else if(value instanceof Boolean) {
168+
return JsonNodeFactory.instance.booleanNode( ((Boolean)value).booleanValue());
169+
} else if(value instanceof Date) {
170+
return JsonNodeFactory.instance.textNode(Constants.getDateFormat().format((Date)value));
171+
} else {
172+
return JsonNodeFactory.instance.textNode(value.toString());
173+
}
174+
}
175+
176+
/**
177+
* Converts a json document to an object tree of maps/lists/values
178+
*
179+
* If json is a value, then the corresponding Java object is returned.
180+
*
181+
* If json is an array, a List is returned. Elements of the list are converted recursively
182+
*
183+
* If json is an object, a Map is returned.
184+
*/
185+
public static Object fromJson(JsonNode json) {
186+
if(json==null||json instanceof NullNode) {
187+
return null;
188+
} else if(json instanceof ObjectNode) {
189+
return fromJson((ObjectNode)json);
190+
} else if(json instanceof ArrayNode) {
191+
return fromJson((ArrayNode)json);
192+
} else {
193+
return valueFromJson( (ValueNode)json);
194+
}
195+
}
196+
197+
private static Object fromJson(ObjectNode json) {
198+
HashMap ret=new HashMap();
199+
for(Iterator<Map.Entry<String,JsonNode>> itr=json.fields();itr.hasNext();) {
200+
Map.Entry<String,JsonNode> entry=itr.next();
201+
ret.put(entry.getKey(),fromJson(entry.getValue()));
202+
}
203+
return ret;
204+
}
205+
206+
private static Object fromJson(ArrayNode json) {
207+
ArrayList ret=new ArrayList(json.size());
208+
for(Iterator<JsonNode> itr=json.elements();itr.hasNext();) {
209+
ret.add(fromJson(itr.next()));
210+
}
211+
return ret;
212+
}
213+
214+
/**
215+
* Converts a Java object tree containing maps and collections to json
216+
*/
217+
public static JsonNode toJson(Object obj) {
218+
if(obj==null) {
219+
return JsonNodeFactory.instance.nullNode();
220+
} else if(obj instanceof Map) {
221+
return toJson( (Map)obj );
222+
} else if(obj instanceof Collection) {
223+
return toJson( (Collection)obj);
224+
} else {
225+
return valueToJson(obj);
226+
}
227+
}
228+
229+
private static JsonNode toJson(Map obj) {
230+
ObjectNode node=JsonNodeFactory.instance.objectNode();
231+
for(Iterator<Map.Entry> itr=obj.entrySet().iterator();itr.hasNext();) {
232+
Map.Entry entry=itr.next();
233+
node.set((String)entry.getKey(),toJson(entry.getValue()));
234+
}
235+
return node;
236+
}
237+
238+
private static JsonNode toJson(Collection obj) {
239+
ArrayNode node=JsonNodeFactory.instance.arrayNode();
240+
for(Object x:obj) {
241+
node.add(toJson(x));
242+
}
243+
return node;
244+
}
245+
111246
/**
112247
* Pretty print a json doc
113248
*/

util/src/test/java/com/redhat/lightblue/util/JsonUtilsTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
*/
1919
package com.redhat.lightblue.util;
2020

21+
import java.util.Map;
22+
import java.util.List;
23+
import java.util.HashMap;
24+
import java.util.ArrayList;
25+
2126
import com.fasterxml.jackson.databind.JsonNode;
2227
import com.fasterxml.jackson.databind.ObjectMapper;
2328
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
@@ -120,4 +125,29 @@ public void testJsonWithInvalidJSONInput() throws Exception {
120125
// TODO review the generated test code and remove the default call to fail.
121126
fail("The test case is a prototype.");
122127
}
128+
129+
@Test
130+
public void testFromJson() throws Exception {
131+
JsonNode node=JsonUtils.json(getClass().getResourceAsStream("/JsonNodeDocTest-complex.json"));
132+
Map obj=(Map)JsonUtils.fromJson(node);
133+
Assert.assertTrue(obj.get("object1") instanceof Map);
134+
Assert.assertTrue(obj.get("object2") instanceof Map);
135+
Assert.assertEquals("value3",((Map)obj.get("object2")).get("simple3"));
136+
Assert.assertEquals(2,((List) ((Map)obj.get("object1")).get("array1")).size());
137+
Assert.assertEquals("value1",((Map)((List)((Map)obj.get("object1")).get("array1")).get(0)).get("simple1"));
138+
}
139+
140+
@Test
141+
public void testToson() throws Exception {
142+
HashMap m=new HashMap();
143+
m.put("v1","value1");
144+
HashMap n;
145+
m.put("o",n=new HashMap());
146+
List a=new ArrayList();
147+
n.put("a",a);
148+
a.add(new Integer(1));
149+
JsonNode node=JsonUtils.toJson(m);
150+
Assert.assertEquals("value1",node.get("v1").asText());
151+
Assert.assertEquals(1,node.get("o").get("a").get(0).asInt());
152+
}
123153
}

0 commit comments

Comments
 (0)