Jackson模拟对PHP序列化反序列化

Jackson模拟对PHP序列化反序列化

模拟结果

---------------{null}-------------
serializer -->  think_serialize:N;
deserialize -->  null

---------------{String}-------------
serializer -->  think_serialize:s:27:"foobar好汉좋아요よし";
deserialize -->  foobar好汉좋아요よし

---------------{boolean}-------------
serializer -->  think_serialize:b:1;
deserialize -->  true

---------------{int}-------------
serializer -->  think_serialize:i:10;
deserialize -->  10

---------------{long}-------------
serializer -->  think_serialize:i:1722590532191;
deserialize -->  1722590532191

---------------{double}-------------
serializer -->  think_serialize:d:10.0;
deserialize -->  10.0

---------------{String[]}-------------
serializer -->  think_serialize:a:2:{i:0;s:27:"foobar好汉좋아요よし";i:1;s:1:"b";}
foobar好汉좋아요よし
b

---------------{List}-------------
serializer -->  think_serialize:a:3:{i:0;s:27:"foobar好汉좋아요よし";i:1;s:1:"b";i:2;s:1:"c";}
foobar好汉좋아요よし
b
c

---------------{MyClass}-------------
serializer -->  think_serialize:a:4:{s:3:"foo";s:4:"foo1";s:3:"bar";i:1;s:4:"tags";a:2:{i:0;s:2:"c1";i:1;s:2:"d1";}s:7:"myClass";a:4:{s:3:"foo";s:4:"foo2";s:3:"bar";i:2;s:4:"tags";a:2:{i:0;s:2:"c2";i:1;s:2:"d2";}s:7:"myClass";a:4:{s:3:"foo";s:27:"foobar好汉좋아요よし";s:3:"bar";i:3;s:4:"tags";a:2:{i:0;s:27:"foobar好汉좋아요よし";i:1;s:2:"d3";}s:7:"myClass";N;}}}

{"foo":"foo1","bar":1,"tags":["c1","d1"],"myClass":{"foo":"foo2","bar":2,"tags":["c2","d2"],"myClass":{"foo":"foobar好汉좋아요よし","bar":3,"tags":["foobar好汉좋아요よし","d3"],"myClass":null}}}

---------------{ObjectNode}-------------
serializer -->  think_serialize:a:3:{s:8:"foodInfo";a:4:{s:3:"bar";i:2;s:3:"foo";s:4:"foo2";s:7:"object1";a:2:{s:27:"foobar好汉좋아요よし";i:2;s:8:"foodInfo";s:27:"foobar好汉좋아요よし";}s:5:"array";a:3:{i:0;s:2:"aa";i:1;s:3:"bbb";i:2;s:2:"cc";}}s:9:"tableInfo";a:3:{i:0;s:2:"aa";i:1;s:3:"bbb";i:2;s:2:"cc";}s:4:"name";s:8:"zhangsan";}

{"foodInfo":{"bar":2,"foo":"foo2","object1":{"foobar好汉좋아요よし":2,"foodInfo":"foobar好汉좋아요よし"},"array":["aa","bbb","cc"]},"tableInfo":["aa","bbb","cc"],"name":"zhangsan"}

PhpGenerator

package org.anycu.common.core.jackson.dataformat;

import com.fasterxml.jackson.core.Base64Variant;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.base.GeneratorBase;
import com.fasterxml.jackson.core.json.JsonWriteContext;

import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;

public class PhpGenerator extends GeneratorBase {

    private static final String DELIM = ":";
    private static final String EOR = ";";

    private final Writer writer;
    private PhpWriteContext context;

    public PhpGenerator(int features, ObjectCodec codec, Writer out) {
        super(features, codec);
        this.writer = out;
        this.context = PhpWriteContext.createRootContext(out);
    }

    @Override
    public Object getOutputTarget() {
        return this.writer;
    }

    @Override
    public void writeStartArray() throws IOException {
        writeArrayIndexIfArray();
        this.context = this.context.createChildArrayContext();
    }

    @Override
    public void writeEndArray() throws IOException {
        if (!context.inArray()) {
            _reportError("Current context not an ARRAY but " + context.getTypeDesc());
        }
        this.flushArray();
    }

    @Override
    public void writeStartObject() throws IOException {
        writeArrayIndexIfArray();
        context = context.createChildObjectContext();
    }

    @Override
    public void writeEndObject() throws IOException {
        if (!context.inObject()) {
            _reportError("Current context not an object but " + context.getTypeDesc());
        }
        this.flushArray();
    }

    @Override
    public void writeFieldName(String name) throws IOException {
        int status = context.writeFieldName(name);
        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
            _reportError("Can not write a field name, expecting a value");
        }
        writeString(name);
    }

    @Override
    public void writeFieldName(SerializableString name) throws IOException {
        writeFieldName(name.getValue());
    }

    @Override
    public void writeString(String text) throws IOException {
        writeArrayIndexIfArray();

        Integer count = byteCount(text);

        this.context.append("s");
        this.context.append(DELIM);
        this.context.append(count.toString());
        this.context.append(DELIM);
        this.context.append("\"");
        this.context.append(text);
        this.context.append("\"");
        this.context.append(EOR);
        this.context.writeValue();
    }

    @Override
    public void writeString(char[] text, int offset, int len) throws IOException {
        writeString(new String(text).substring(offset, len));
    }

    @Override
    public void flush() throws IOException {
        writer.flush();
    }

    @Override
    protected void _releaseBuffers() {
        throw new UnsupportedOperationException("Not supported release buffers.");
    }

    @Override
    protected void _verifyValueWrite(String typeMsg) throws IOException {
        // nothing to do
    }

    @Override
    public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException {
        throw new UnsupportedOperationException("Not supported write raw UTF8 String.");
    }

    @Override
    public void writeUTF8String(byte[] text, int offset, int length) throws IOException {
        throw new UnsupportedOperationException("Not supported write UTF8 String.");
    }

    @Override
    public void writeRaw(String text) throws IOException {
        throw new UnsupportedOperationException("Not supported write raw.");
    }

    @Override
    public void writeRaw(String text, int offset, int len) throws IOException {
        throw new UnsupportedOperationException("Not supported write raw String.");
    }

    @Override
    public void writeRaw(char[] text, int offset, int len) throws IOException {
        throw new UnsupportedOperationException("Not supported write raw char[].");
    }

    @Override
    public void writeRaw(char c) throws IOException {
        throw new UnsupportedOperationException("Not supported write raw char.");
    }

    @Override
    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException {
        throw new UnsupportedOperationException("Not supported write binary.");
    }

    @Override
    public void writeNumber(int v) throws IOException {
        this.writeInt(v);
    }

    @Override
    public void writeNumber(long v) throws IOException {
        this.writeInt(v);
    }

    @Override
    public void writeNumber(BigInteger v) throws IOException {
        this.writeInt(v);
    }

    @Override
    public void writeNumber(double d) throws IOException {
        writeDouble(d);
    }

    @Override
    public void writeNumber(float f) throws IOException {
        writeDouble(f);
    }

    @Override
    public void writeNumber(BigDecimal dec) throws IOException {
        writeDouble(dec);
    }

    @Override
    public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException {
        throw new UnsupportedOperationException("Not supported write number String.");
    }

    @Override
    public void writeBoolean(boolean state) throws IOException {
        writeArrayIndexIfArray();

        context.append("b");
        context.append(DELIM);
        context.append(state ? "1" : "0");
        context.append(EOR);
        context.writeValue();
    }

    @Override
    public void writeNull() throws IOException {
        writeArrayIndexIfArray();

        this.context.append("N");
        this.context.append(EOR);
        this.context.writeValue();
    }

    private void writeArrayIndexIfArray() throws IOException {
        if (this.context.inArray()) {
            this.context.append("i");
            this.context.append(DELIM);
            this.context.append(Integer.toString(this.context.getFieldCount()));
            this.context.append(EOR);
            this.context.incrementFieldCount();
        }
    }

    private void writeInt(Number v) throws IOException {
        writeArrayIndexIfArray();

        this.context.append("i");
        this.context.append(DELIM);
        this.context.append(v.toString());
        this.context.append(EOR);
        this.context.writeValue();
    }

    private void writeDouble(Number v) throws IOException {
        writeArrayIndexIfArray();

        this.context.append("d");
        this.context.append(DELIM);
        this.context.append(v.toString());
        this.context.append(EOR);
        this.context.writeValue();
    }

    private void flushArray() throws IOException {
        this.context.getWriter().close();
        Writer prefixWriter = context.getParent().getWriter();
        prefixWriter.append("a");
        prefixWriter.append(DELIM);
        prefixWriter.append(Integer.toString(context.getFieldCount()));
        prefixWriter.append(DELIM);
        prefixWriter.append("{");
        prefixWriter.append(context.getWriter().toString());
        prefixWriter.append("}");
        this.context = this.context.getParent();
    }

    /**
     * 字节统计
     */
    public int byteCount(String text) {
        int count = 0;
        char[] chars = text.toCharArray();
        for (char ch : chars) {
            if ((ch >= 0x0001) && (ch <= 0x007F)) {
                count++;
            } else if (ch > 0x07FF) {
                count += 3;
            } else {
                count += 2;
            }
        }
        return count;
    }
}

PhpParser

package org.anycu.common.core.jackson.dataformat;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserMinimalBase;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.io.NumberInput;
import com.fasterxml.jackson.core.json.JsonReadContext;
import com.fasterxml.jackson.core.json.PackageVersion;

import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;

public class PhpParser extends ParserMinimalBase {

    private static final char[] EXPECTED_CHARS = new char[]{'i', ':', '0', ';'};
    private static final int INT_SEMICOLON = ';';

    private final IOContext ioContext;
    private final PushbackReader reader;

    private boolean closed;
    private ObjectCodec objectCodec;
    private JsonReadContext parsingContext;

    private String currentValue;
    private int numTypesValid = NR_UNKNOWN;
    private int numberInt;
    private long numberLong;
    private BigInteger numberBigInt;

    public PhpParser(IOContext iOContext, Reader reader, int features, ObjectCodec objectCodec) {
        super(features);
        this.objectCodec = objectCodec;
        this.reader = new PushbackReader(reader, EXPECTED_CHARS.length);
        this.ioContext = iOContext;
        this.parsingContext = JsonReadContext.createRootContext(null);
    }

    @Override
    public JsonToken nextToken() throws IOException {
        if (this.closed) {
            return null;
        }

        int i = this.reader.read();
        if (i == INT_RCURLY) {
            _currToken = this.parsingContext.inObject() ?
                    JsonToken.END_OBJECT : JsonToken.END_ARRAY;

            this.parsingContext = this.parsingContext.getParent();
            return _currToken;
        }

        if (this.parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
            determineNextToken(i);
            this.parsingContext.setCurrentName(currentValue);
            _currToken = JsonToken.FIELD_NAME;
            return _currToken;
        }

        if (this.parsingContext.inArray() && !isExpectedStartArrayToken()) {
            consume(INT_COLON);
            consumeValuesAndExpected(INT_SEMICOLON);
            i = this.reader.read();
        }

        _currToken = determineNextToken(i);
        return _currToken;
    }

    private JsonToken determineNextToken(int i) throws IOException {
        JsonToken t = null;
        switch ((char) i) {
            case 'i':
                t = parseInt();
                break;
            case 'd':
                t = parseFloat();
                break;
            case 'b':
                t = parseBoolean();
                break;
            case 's':
                t = parseString();
                consume(INT_SEMICOLON);
                break;
            case 'a':
                consume(INT_COLON);
                consumeValuesAndExpected(INT_COLON);
                consume(INT_LCURLY);

                /**
                 * 在php中数组对应java中对象和数组;
                 * 此处没有想到更好的方式;试着预读后面字段的类型i和s区分吧;如果期望出现时 "i:0;" 就认定是数组;
                 */
                if (isExpectedStartArray()) {
                    this.parsingContext = this.parsingContext.createChildArrayContext(0, 0);
                    t = JsonToken.START_ARRAY;
                } else {
                    this.parsingContext = this.parsingContext.createChildObjectContext(0, 0);
                    t = JsonToken.START_OBJECT;
                }
                break;
            case 'N':
                t = JsonToken.VALUE_NULL;
                consume(INT_SEMICOLON);
                break;
            default:
                _reportError("Unknown type: '" + ((char) i) + "'");
        }
        return t;
    }

    @Override
    public NumberType getNumberType() throws IOException {
        if (_currToken == JsonToken.VALUE_NUMBER_INT) {
            final String text = currentValue.trim();
            final boolean numberNegative = isNumberNegative(text);
            final int len = isIntNumber(text);

            if (len > 0) {
                if (len <= 9) {
                    numberInt = NumberInput.parseInt(text);
                    numTypesValid = NR_INT;
                    return NumberType.INT;
                } else if (len <= 18) {
                    long l = NumberInput.parseLong(text);
                    if (len == 10) {
                        if (numberNegative) {
                            if (l >= MIN_INT_L) {
                                numberInt = (int) l;
                                numTypesValid = NR_INT;
                                return NumberType.INT;
                            }
                        } else {
                            if (l <= MAX_INT_L) {
                                numberInt = (int) l;
                                numTypesValid = NR_INT;
                                return NumberType.INT;
                            }
                        }
                    }
                    numberLong = l;
                    numTypesValid = NR_LONG;
                    return NumberType.LONG;
                } else if (len == 19) {
                    final boolean stillLong = NumberInput.inLongRange(numberNegative ?
                            text.substring(1) : text, numberNegative);

                    if (stillLong) {
                        numberLong = NumberInput.parseLong(text);
                        numTypesValid = NR_LONG;
                        return NumberType.LONG;
                    }
                } else {
                    streamReadConstraints().validateIntegerLength(text.length());
                    numberBigInt = NumberInput.parseBigInteger(
                            text, isEnabled(StreamReadFeature.USE_FAST_BIG_NUMBER_PARSER));
                    numTypesValid = NR_BIGINT;
                    return NumberType.BIG_INTEGER;
                }
            }
        } else if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
            return NumberType.DOUBLE;
        } else {
            _reportError("Current token (%s) not numeric, can not use numeric value accessors", this._currToken);
        }
        return null;
    }

    private boolean isNumberNegative(String text) {
        final int len = text.length();
        if (len > 0) {
            return text.charAt(0) == '-';
        }
        return false;
    }

    protected final int isIntNumber(String text) {
        final int len = text.length();
        if (len > 0) {
            char c = text.charAt(0);
            final int start = (c == '-') ? 1 : 0;
            for (int i = start; i < len; ++i) {
                int ch = text.charAt(i);
                if (ch > '9' || ch < '0') {
                    return -1;
                }
            }
            return len - start;
        }
        return 0;
    }

    @Override
    protected void _handleEOF() throws JsonParseException {
        throw new UnsupportedOperationException("Not supported method#_handleEOF.");
    }

    @Override
    public String getCurrentName() throws IOException {
        if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
            JsonReadContext parent = this.parsingContext.getParent();
            return parent.getCurrentName();
        }
        return this.parsingContext.getCurrentName();
    }

    @Override
    public ObjectCodec getCodec() {
        return this.objectCodec;
    }

    @Override
    public void setCodec(ObjectCodec oc) {
        this.objectCodec = oc;
    }

    @Override
    public Version version() {
        return PackageVersion.VERSION;
    }

    @Override
    public void close() throws IOException {
        if (!this.closed) {
            this.closed = true;
            this.reader.close();
        }
    }

    @Override
    public boolean isClosed() {
        return this.closed;
    }

    @Override
    public JsonStreamContext getParsingContext() {
        return this.parsingContext;
    }

    @Override
    public JsonLocation getCurrentLocation() {
        return new JsonLocation(ioContext == null ? null : ioContext.contentReference(), 0, 0, 0);
    }

    @Override
    public JsonLocation getTokenLocation() {
        return JsonLocation.NA;
    }

    @Override
    public void overrideCurrentName(String name) {
        throw new UnsupportedOperationException("Not supported method#overrideCurrentName.");
    }

    @Override
    public String getText() throws IOException {
        return this.currentValue;
    }

    @Override
    public char[] getTextCharacters() throws IOException {
        return this.currentValue.toCharArray();
    }

    @Override
    public boolean hasTextCharacters() {
        return this.currentValue != null && this.currentValue.length() > 0;
    }

    @Override
    public Number getNumberValue() throws IOException {
        if ((numTypesValid & NR_INT) != 0) {
            return numberInt;
        }
        if ((numTypesValid & NR_LONG) != 0) {
            return numberLong;
        }
        if ((numTypesValid & NR_BIGINT) != 0) {
            return numberBigInt;
        }
        return null;
    }

    @Override
    public int getIntValue() throws IOException {
        return numberInt;
    }

    @Override
    public long getLongValue() throws IOException {
        return numberLong;
    }

    @Override
    public BigInteger getBigIntegerValue() throws IOException {
        return numberBigInt;
    }

    @Override
    public float getFloatValue() throws IOException {
        return Float.parseFloat(this.currentValue);
    }

    @Override
    public double getDoubleValue() throws IOException {
        return Double.parseDouble(this.currentValue);
    }

    @Override
    public BigDecimal getDecimalValue() throws IOException {
        return new BigDecimal(this.currentValue);
    }

    @Override
    public int getTextLength() throws IOException {
        return this.currentValue.length();
    }

    @Override
    public String getValueAsString() throws IOException {
        return this.currentValue;
    }

    @Override
    public int getTextOffset() throws IOException {
        throw new UnsupportedOperationException("Not supported method#getTextOffset.");
    }

    @Override
    public byte[] getBinaryValue(Base64Variant b64variant) throws IOException {
        throw new UnsupportedOperationException("Not supported method#getBinaryValue.");
    }

    private JsonToken parseString() throws IOException {
        int strLen = readValueLength();

        int ch = this.reader.read();
        if (ch != '"') {
            reportUnexpectedChar(ch, '"');
        }

        StringBuilder b = new StringBuilder(strLen);
        int byteCount = 0;
        while (byteCount != strLen) {
            ch = this.reader.read();
            b.append((char) ch);

            if ((ch >= 0x0001) && (ch <= 0x007F)) {
                byteCount++;
            } else if (ch > 0x07FF) {
                byteCount += 3;
            } else {
                byteCount += 2;
            }
        }
        this.currentValue = b.toString();
        ch = this.reader.read();
        if (ch != '"') {
            reportUnexpectedChar(ch, '"');
        }
        return JsonToken.VALUE_STRING;
    }

    private String readNextValue() throws IOException {
        int c = this.reader.read();
        if (c != INT_COLON) {
            reportUnexpectedChar(c, INT_COLON);
        }
        StringBuilder b = new StringBuilder();
        c = this.reader.read();
        while (c != -1 && c != INT_SEMICOLON) {
            b.append((char) c);
            c = this.reader.read();
        }
        return b.toString();
    }

    private JsonToken parseBoolean() throws IOException {
        String nextValue = readNextValue();

        if ("1".equals(nextValue)) {
            this.currentValue = "true";
            return JsonToken.VALUE_TRUE;
        } else if ("0".equals(nextValue)) {
            this.currentValue = "false";
            return JsonToken.VALUE_FALSE;
        } else {
            this.currentValue = nextValue;
            return Boolean.parseBoolean(nextValue) ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
        }
    }

    private JsonToken parseFloat() throws IOException {
        this.currentValue = readNextValue();
        return JsonToken.VALUE_NUMBER_FLOAT;
    }

    private JsonToken parseInt() throws IOException {
        this.currentValue = readNextValue();
        return JsonToken.VALUE_NUMBER_INT;
    }

    /**
     * 读取下一个字段的长度
     */
    private int readValueLength() throws IOException {
        int c = this.reader.read();
        if (c != INT_COLON) {
            return 0;
        }
        StringBuilder b = new StringBuilder();
        c = this.reader.read();
        while (c != -1 && c != INT_COLON) {
            b.append((char) c);
            c = this.reader.read();
        }
        if (b.length() > 0) {
            return Integer.parseInt(b.toString());
        }
        return 0;
    }

    /**
     * 预读后面4位,判断是否数组开头,否则推回已读取的字符
     */
    private boolean isExpectedStartArray() throws IOException {
        int length = EXPECTED_CHARS.length;

        char[] buffer = new char[length];
        int numRead = 0;
        // 逐字符读取并检查
        for (int i = 0; i < length; i++) {
            int ch = this.reader.read();
            if (ch == -1) {
                // 如果到达流末尾,推回已读取的字符
                if (numRead > 0) {
                    this.reader.unread(buffer, 0, numRead);
                }
                return false;
            }
            buffer[i] = (char) ch;
            numRead++;
            if (buffer[i] != EXPECTED_CHARS[i]) {
                // 如果字符不匹配,推回已读取的字符
                this.reader.unread(buffer, 0, numRead);
                return false;
            }
        }
        return true;
    }

    /**
     * 消费连续数字并期望接下来是val字符
     * 例如:3:  0;
     */
    private void consumeValuesAndExpected(int val) throws IOException {
        int ch = this.reader.read();
        while (ch != -1 && ch <= '9' && ch >= '0') {
            ch = this.reader.read();
        }
        if (ch != -1 && ch != val) {
            reportUnexpectedChar(ch, val);
        }
    }

    private void consume(int val) throws IOException {
        int ch = this.reader.read();
        if (ch != -1 && ch != val) {
            reportUnexpectedChar(ch, val);
        }
    }

    private void reportUnexpectedChar(int got, int expected) throws JsonParseException {
        _reportError("Expected " + ((char) expected) + " but got '" + ((char) got) + "'");
    }
}

PhpWriteContext

package org.anycu.common.core.jackson.dataformat;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonWriteContext;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

public class PhpWriteContext {

    private final JsonWriteContext jsonContext;
    private final PhpWriteContext parent;

    private PhpWriteContext child;
    private Writer writer;
    private int fieldCount = 0;

    protected PhpWriteContext(JsonWriteContext jsonContext, PhpWriteContext parent, Writer writer) {
        this.jsonContext = jsonContext;
        this.parent = parent;
        this.writer = writer;
        if (this.writer == null) {
            this.writer = new StringWriter();
        }
    }

    public static PhpWriteContext createRootContext(Writer writer) {
        return new PhpWriteContext(JsonWriteContext.createRootContext(null), null, writer);
    }

    public final PhpWriteContext createChildArrayContext() {
        this.child = new PhpWriteContext(jsonContext.createChildArrayContext(), this, null);
        return this.child;
    }

    public final PhpWriteContext createChildObjectContext() {
        this.child = new PhpWriteContext(jsonContext.createChildObjectContext(), this, null);
        return this.child;
    }

    public final PhpWriteContext getParent() {
        return this.parent;
    }

    public final int writeFieldName(String name) throws JsonProcessingException {
        this.fieldCount++;
        return this.jsonContext.writeFieldName(name);
    }

    public final int writeValue() {
        return this.jsonContext.writeValue();
    }

    public int getFieldCount() {
        return this.fieldCount;
    }

    public void incrementFieldCount() {
        this.fieldCount++;
    }

    public Writer getWriter() {
        return this.writer;
    }

    public void append(CharSequence string) throws IOException {
        this.writer.append(string);
    }

    public boolean inObject() {
        return this.jsonContext.inObject();
    }

    public boolean inArray() {
        return this.jsonContext.inArray();
    }

    public String getTypeDesc() {
        return this.jsonContext.typeDesc();
    }
}

PhpJsonFactory

package org.anycu.common.core.jackson.dataformat;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.format.InputAccessor;
import com.fasterxml.jackson.core.format.MatchStrength;
import com.fasterxml.jackson.core.io.IOContext;

import java.io.*;
import java.net.URL;

public class PhpJsonFactory extends JsonFactory {

    public final static String FORMAT_NAME_PHP = "PHP";

    @Override
    public String getFormatName() {
        return FORMAT_NAME_PHP;
    }

    @Override
    public MatchStrength hasFormat(InputAccessor acc) throws IOException {
        return MatchStrength.FULL_MATCH;
    }

    /**
     * **************************************
     * Overridden Generator factory methods
     * **************************************
     */

    @Override
    public PhpGenerator createGenerator(OutputStream out) throws IOException {
        return this.createGenerator(out, JsonEncoding.UTF8);
    }

    @Override
    public PhpGenerator createGenerator(OutputStream out, JsonEncoding encode) throws IOException {
        IOContext context = _createContext(_createContentReference(out), false);
        context.setEncoding(encode);
        return _createGenerator(this._createWriter(out, encode), context);
    }

    @Override
    protected PhpGenerator _createGenerator(Writer out, IOContext context) throws IOException {
        return new PhpGenerator(0, _objectCodec, out);
    }

    protected Writer _createWriter(OutputStream out, JsonEncoding encode) throws IOException {
        return new OutputStreamWriter(out, encode.getJavaName());
    }

    /**
     * **************************************
     * Overridden Parser factory methods
     * **************************************
     */
    @Override
    public PhpParser createParser(byte[] data) throws IOException {
        IOContext context = _createContext(_createContentReference(data), Boolean.TRUE);
        return this._createParser(data, 0, data.length, context);
    }

    @Override
    public PhpParser createParser(byte[] data, int offset, int len) throws IOException {
        IOContext context = _createContext(_createContentReference(data, offset, len), Boolean.TRUE);
        return this._createParser(data, offset, len, context);
    }

    @Override
    public PhpParser createParser(File f) throws IOException {
        IOContext context = _createContext(_createContentReference(f), Boolean.TRUE);
        InputStream inputStream = new FileInputStream(f);
        return this._createParser(inputStream, context);
    }

    @Override
    public PhpParser createParser(InputStream in) throws IOException {
        IOContext context = _createContext(_createContentReference(in), Boolean.FALSE);
        return this._createParser(in, context);
    }

    @Override
    public PhpParser createParser(Reader r) {
        IOContext context = _createContext(_createContentReference(r), Boolean.TRUE);
        return this._createParser(r, context);
    }

    @Override
    public PhpParser createParser(String content) {
        return this.createParser(new StringReader(content));
    }

    @Override
    public PhpParser createParser(URL url) throws IOException {
        IOContext context = _createContext(_createContentReference(url), Boolean.TRUE);
        InputStream inputStream = _optimizedStreamFromURL(url);
        return this._createParser(inputStream, context);
    }

    @Override
    protected PhpParser _createParser(byte[] data, int offset, int len, IOContext context) throws IOException {
        JsonEncoding enc = context.getEncoding();
        Reader reader = this._createReader(data, offset, len, enc);
        return new PhpParser(context, reader, _parserFeatures, _objectCodec);
    }

    @Override
    protected PhpParser _createParser(InputStream in, IOContext context) throws IOException {
        JsonEncoding enc = context.getEncoding();
        Reader reader = this._createReader(in, enc);
        return new PhpParser(context, reader, _parserFeatures, _objectCodec);
    }

    @Override
    protected PhpParser _createParser(Reader reader, IOContext context) {
        return new PhpParser(context, reader, _parserFeatures, _objectCodec);
    }

    protected Reader _createReader(byte[] data, int offset, int len, JsonEncoding jsonEncoding) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data, offset, len);
        return new InputStreamReader(byteArrayInputStream, jsonEncoding.getJavaName());
    }

    protected Reader _createReader(InputStream inputStream, JsonEncoding encoding) throws IOException {
        return new InputStreamReader(inputStream, encoding.getJavaName());
    }
}

PhpJacksonUtils

package org.anycu.common.core.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.experimental.UtilityClass;
import org.anycu.common.core.jackson.dataformat.PhpJsonFactory;
import org.apache.commons.lang3.StringUtils;

import java.io.UncheckedIOException;

@UtilityClass
public class PhpJacksonUtils {

    private static final String PREFIX = "think_serialize:";
    private static final ObjectMapper PHP_MAPPER = new ObjectMapper(new PhpJsonFactory());

    /**
     * 序列化
     */
    public static String serializer(Object value) {
        try {
            return PREFIX + PHP_MAPPER.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * 反序列化
     * <p>
     * 注意:
     * 1>尚未完全解决序列化数组问题,目前粗暴判定当出现数组'i:0;'认定为数组;而最理想判断连续下标为数组;
     * 所以特殊情况下int类型作为key且刚好等于0的情况;此处特别想【🔨💣🐔】
     * 2>目前业务key-value中;key都是String表现;基本满足要求;
     */
    public static JsonNode deserialize(String value) {
        if (StringUtils.isEmpty(value)) {
             return NullNode.getInstance();
        }
        if (StringUtils.startsWith(value, PREFIX)) {
            value = value.substring(PREFIX.length());
        }
        try {
            return PHP_MAPPER.readTree(value);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new UncheckedIOException(e);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值