我想交换JSON字符串中元素的位置。这样做最有效的方法是什么? Jackson或Gson是否提供任何功能来改变JSON字符串中元素的位置?
之前:
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
在:
{
"lastName": "Smith",
"firstName": "John",
"age": 25,
"address": {
"city": "New York",
"streetAddress": "21 2nd Street",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
答案 0 :(得分:1)
您可以在Java类中使用Jackson JsonPropertyOrder
。
@JsonPropertyOrder({ "firstName", "lastName", "age" })
public class MyClass { ... }
答案 1 :(得分:0)
最有效的方法是什么?
通常,答案是:流媒体。流式传输可能有效的原因是您不必将整个值存储在内存中,理论上,您可以处理无限的JSON流。
以下示例是使用Java 8和Gson编写的,但将其移植到Java 7及更低版本很容易。它不接受所有可能的情况,并且由于它没有额外的逻辑,它还交换$.phoneNumbers[].type
和$.phoneNumbers[].number
(或它是否也匹配您的请求?)。无论如何,下面的代码是这样做的想法,可以自己改进。
此实用程序类提供了一种方法,可以从任何读取器读取并写入交换第一个JSON属性的任何编写器(数组被视为具有元素,而不是属性键/值条目)。它以FSM方式实现,因此看起来可能很臃肿。
final class Swap {
private Swap() {
}
static void swapLeadingFields(final Reader from, final Writer to)
throws IOException {
final JsonReader reader = new JsonReader(from);
final JsonWriter writer = new JsonWriter(to);
final State state = new State();
while ( reader.peek() != END_DOCUMENT ) {
final JsonToken token = reader.peek();
switch ( token ) {
case BEGIN_ARRAY:
reader.beginArray();
state.push(ARRAY);
writer.beginArray();
break;
case END_ARRAY:
reader.endArray();
state.pop();
writer.endArray();
break;
case BEGIN_OBJECT:
reader.beginObject();
state.push(OBJECT_BEGIN);
writer.beginObject();
break;
case END_OBJECT:
switch ( state.mode() ) {
case OBJECT_VALUE_1_FOUND:
state.accept(e -> writeProperty(writer, e.key1, e.value1));
break;
case OBJECT_VALUE_2_FOUND:
state.accept(e -> {
writeProperty(writer, e.key2, e.value2);
writeProperty(writer, e.key1, e.value1);
});
break;
case OBJECT_BEGIN:
case OBJECT_SWAPPED:
// do nothing
break;
case OBJECT_KEY_1_FOUND:
case OBJECT_KEY_2_FOUND:
case ARRAY:
throw new IllegalStateException(String.valueOf(state.mode()));
default:
throw new AssertionError(state.mode());
}
reader.endObject();
state.pop();
writer.endObject();
break;
case NAME:
final String name = reader.nextName();
switch ( state.mode() ) {
case OBJECT_BEGIN:
state.accept(e -> {
e.mode = OBJECT_KEY_1_FOUND;
e.key1 = name;
});
break;
case OBJECT_VALUE_1_FOUND:
state.accept(e -> {
e.mode = OBJECT_KEY_2_FOUND;
e.key2 = name;
});
break;
case OBJECT_VALUE_2_FOUND:
state.accept(e -> {
e.mode = OBJECT_SWAPPED;
writeProperty(writer, e.key2, e.value2);
writeProperty(writer, e.key1, e.value1);
});
writer.name(name);
break;
case OBJECT_SWAPPED:
writer.name(name);
break;
case OBJECT_KEY_1_FOUND:
case OBJECT_KEY_2_FOUND:
case ARRAY:
throw new IllegalStateException(String.valueOf(state.mode()));
default:
throw new AssertionError(state.mode());
}
break;
case STRING:
handleSimpleValue(state, reader::nextString, writer::value);
break;
case NUMBER:
handleSimpleValue(state, () -> parseBestGsonNumber(reader.nextString()), n -> writeBestNumber(writer, n));
break;
case BOOLEAN:
handleSimpleValue(state, reader::nextBoolean, writer::value);
break;
case NULL:
handleSimpleValue(state, () -> {
reader.nextNull();
return null;
}, v -> writer.nullValue());
break;
case END_DOCUMENT:
// do nothing
break;
default:
throw new AssertionError(token);
}
}
}
private static <T> void handleSimpleValue(final State state, final ISupplier<? extends T> supplier, final IConsumer<? super T> consumer)
throws IOException {
final T value = supplier.get();
switch ( state.mode() ) {
case OBJECT_KEY_1_FOUND:
state.accept(e -> {
e.mode = OBJECT_VALUE_1_FOUND;
e.value1 = value;
});
break;
case OBJECT_KEY_2_FOUND:
state.accept(e -> {
e.mode = OBJECT_VALUE_2_FOUND;
e.value2 = value;
});
break;
case OBJECT_SWAPPED:
consumer.accept(value);
break;
case OBJECT_BEGIN:
case OBJECT_VALUE_1_FOUND:
case OBJECT_VALUE_2_FOUND:
throw new IllegalStateException(String.valueOf(state.mode()));
case ARRAY:
default:
throw new AssertionError(state.mode());
}
}
private static void writeProperty(final JsonWriter writer, final String key, final Object value)
throws IOException {
writer.name(key);
if ( value instanceof String ) {
writer.value((String) value);
} else if ( value instanceof Number ) {
writeBestNumber(writer, (Number) value);
} else if ( value instanceof Boolean ) {
writer.value((boolean) value);
} else {
throw new AssertionError(value.getClass());
}
}
private static void writeBestNumber(final JsonWriter writer, final Number number)
throws IOException {
if ( number instanceof Double ) {
writer.value((double) number);
} else if ( number instanceof Long ) {
writer.value((long) number);
} else {
writer.value(number);
}
}
}
原则上,州级只是一个带有一些状态友好方法的数据包。关于为什么它具有accept(IConsumer)
而不是current()
:switch
es(特别是嵌套的)的说明没有为局部变量提供良好的范围隔离机制。
final class State {
enum Mode {
OBJECT_BEGIN,
OBJECT_KEY_1_FOUND,
OBJECT_VALUE_1_FOUND,
OBJECT_KEY_2_FOUND,
OBJECT_VALUE_2_FOUND,
OBJECT_SWAPPED,
ARRAY
}
static final class Element {
Mode mode;
String key1;
Object value1;
String key2;
Object value2;
private Element(final Mode mode) {
this.mode = mode;
}
}
private final Stack<Element> stack = new Stack<>();
void push(final Mode mode) {
stack.push(new Element(mode));
}
void pop() {
stack.pop();
}
Mode mode() {
return stack.peek().mode;
}
void accept(final IConsumer<? super Element> consumer)
throws IOException {
consumer.accept(stack.peek());
}
}
这只是一个帮助类,试图检测&#34;最窄的&#34;数据类型以最简单的方式,但它不一定有助于保持源和目标的相同文字类型(例如,reader.nextDouble()
可能会导致25.0
和10021.0
结果)。
final class Numbers {
private Numbers() {
}
static Number parseBestGsonNumber(final String rawNumber) {
try {
return Integer.parseInt(rawNumber);
} catch ( final NumberFormatException exParseInt ) {
try {
return Long.parseLong(rawNumber);
} catch ( final NumberFormatException exParseLong ) {
return Double.parseDouble(rawNumber);
}
}
}
}
以下接口只在JDK 8中具有未经检查的异常对应,并且可以在必要时进行增强。
interface IConsumer<T> {
void accept(T t)
throws IOException;
}
interface ISupplier<T> {
T get()
throws IOException;
}
演示
public final class EntryPoint {
private EntryPoint() {
}
public static void main(final String... args)
throws IOException {
try ( InputStream inputStream = currentThread().getContextClassLoader().getResourceAsStream("data.json");
final Reader from = new InputStreamReader(inputStream) ) {
final Writer to = new OutputStreamWriter(System.out);
try {
swapLeadingFields(from, to);
} finally {
to.flush();
}
}
}
}
漂亮的印刷结果:
{
"lastName": "Smith",
"firstName": "John",
"age": 25,
"address": {
"city": "New York",
"streetAddress": "21 2nd Street",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [
{
"number": "212 555-1234",
"type": "home"
},
{
"number": "646 555-4567",
"type": "fax"
}
]
}